package co.yixiang.yshop.module.order.service.storeorder;

import cn.hutool.core.util.*;
import co.yixiang.yshop.framework.common.constant.CartConstant;
import co.yixiang.yshop.framework.common.enums.OrderDefaultRuleEnum;
import co.yixiang.yshop.framework.common.enums.OrderInfoEnum;
import co.yixiang.yshop.framework.common.enums.UserTypeEnum;
import co.yixiang.yshop.framework.common.exception.ErrorCode;
import co.yixiang.yshop.framework.common.util.json.JsonUtils;
import co.yixiang.yshop.framework.dict.core.util.DictFrameworkUtils;
import co.yixiang.yshop.framework.redis.util.RedissonUtil;
import co.yixiang.yshop.framework.security.core.LoginUser;
import co.yixiang.yshop.module.cart.dal.dataobject.storecart.StoreCartDO;
import co.yixiang.yshop.module.cart.service.storecart.AppStoreCartService;
import co.yixiang.yshop.module.distributor.api.dto.DistributorOrderDTO;
import co.yixiang.yshop.module.distributor.enums.DistributorSettlementTypeEnum;
import co.yixiang.yshop.module.member.controller.admin.userbill.vo.UserBillModifyDTO;
import co.yixiang.yshop.module.member.controller.app.user.vo.AppUserOrderCountVo;
import co.yixiang.yshop.module.member.controller.app.user.vo.AppUserQueryVo;
import co.yixiang.yshop.module.member.controller.app.userlevel.vo.SettleLevelInfoVO;
import co.yixiang.yshop.module.member.convert.user.UserConvert;
import co.yixiang.yshop.module.member.dal.dataobject.user.MemberUserDO;
import co.yixiang.yshop.module.member.dal.dataobject.useraddress.UserAddressDO;
import co.yixiang.yshop.module.member.enums.BillDetailEnum;
import co.yixiang.yshop.module.member.enums.BillEnum;
import co.yixiang.yshop.module.member.enums.BillTitleEnum;
import co.yixiang.yshop.module.member.service.integralrule.strategy.IntegralRuleContext;
import co.yixiang.yshop.module.member.service.user.MemberUserService;
import co.yixiang.yshop.module.member.service.useraddress.AppUserAddressService;
import co.yixiang.yshop.module.member.service.userbill.UserBillService;
import co.yixiang.yshop.module.member.service.userlevelconfig.UserLevelConfigService;
import co.yixiang.yshop.module.message.enums.WechatTempateEnum;
import co.yixiang.yshop.module.message.mq.message.WeixinNoticeMessage;
import co.yixiang.yshop.module.message.mq.producer.WeixinNoticeProducer;
import co.yixiang.yshop.module.order.controller.app.afterorder.param.AppOrderRefundParam;
import co.yixiang.yshop.module.order.controller.app.order.param.*;
import co.yixiang.yshop.module.order.controller.app.order.vo.*;
import co.yixiang.yshop.module.order.convert.storeorder.StoreOrderConvert;
import co.yixiang.yshop.module.order.dal.dataobject.storeorder.StoreOrderDO;
import co.yixiang.yshop.module.order.dal.dataobject.storeordercartinfo.StoreOrderCartInfoDO;
import co.yixiang.yshop.module.order.dal.dataobject.storeorderdetail.StoreOrderDetailDO;
import co.yixiang.yshop.module.order.dal.dataobject.storeorderstatus.StoreOrderStatusDO;
import co.yixiang.yshop.module.order.dal.mysql.storeorder.StoreOrderMapper;
import co.yixiang.yshop.module.order.dal.mysql.storeorderdetail.StoreOrderDetailMapper;
import co.yixiang.yshop.module.order.dal.mysql.storeorderstatus.StoreOrderStatusMapper;
import co.yixiang.yshop.module.order.dal.redis.order.OrderRedisDAO;
import co.yixiang.yshop.module.order.enums.*;
import co.yixiang.yshop.module.order.service.storeorder.dto.*;
import co.yixiang.yshop.module.order.service.storeordercartinfo.StoreOrderCartInfoService;
import co.yixiang.yshop.module.order.service.storeorderdetail.StoreOrderDetailService;
import co.yixiang.yshop.module.order.service.storeorderrule.StoreOrderRuleService;
import co.yixiang.yshop.module.order.service.storeorderstatus.StoreOrderStatusService;
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
import co.yixiang.yshop.module.pay.enums.PayTypeEnum;
import co.yixiang.yshop.module.pay.service.merchantdetails.MerchantDetailsService;
import co.yixiang.yshop.module.pay.strategy.weixin.service.WxPayServiceUtils;
import co.yixiang.yshop.module.pay.strategy.weixin.skd.PaymentApi;
import co.yixiang.yshop.module.pay.strategy.weixin.skd.RandomStringUtil;
import co.yixiang.yshop.module.product.controller.admin.teamworkinfo.param.TeamworkRefundParam;
import co.yixiang.yshop.module.product.controller.admin.teamworkinfo.vo.TeamworkInfoCreateVO;
import co.yixiang.yshop.module.product.controller.app.cart.vo.AppStoreCartQueryVo;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponDetailVO;
import co.yixiang.yshop.module.product.controller.app.couponrelation.vo.CartCouponVO;
import co.yixiang.yshop.module.product.controller.app.product.vo.AppStoreProductRespVo;
import co.yixiang.yshop.module.product.dal.dataobject.campaigninfo.CampaignInfoDO;
import co.yixiang.yshop.module.product.dal.dataobject.shippingtemplates.ShippingTemplatesDO;
import co.yixiang.yshop.module.product.dal.dataobject.shippingtemplatesfree.ShippingTemplatesFreeDO;
import co.yixiang.yshop.module.product.dal.dataobject.shippingtemplatesregion.ShippingTemplatesRegionDO;
import co.yixiang.yshop.module.product.dal.dataobject.storeproductreply.StoreProductReplyDO;
import co.yixiang.yshop.module.product.enums.campaign.CampaignTypeEnum;
import co.yixiang.yshop.module.product.enums.campaign.TeamworkStateEnum;
import co.yixiang.yshop.module.product.enums.product.StockBackTypeEnum;
import co.yixiang.yshop.module.product.enums.teamworkinfo.VirtuallyEnum;
import co.yixiang.yshop.module.product.service.campaigndetail.CampaignDetailService;
import co.yixiang.yshop.module.product.service.campaigninfo.AppCampaignInfoService;
import co.yixiang.yshop.module.product.service.campaigninfo.CampaignInfoService;
import co.yixiang.yshop.module.product.service.productcouponrelation.AppCouponRelationService;
import co.yixiang.yshop.module.product.service.productcouponrelation.dto.CartCouponDTO;
import co.yixiang.yshop.module.product.service.shippingtemplates.ShippingTemplatesService;
import co.yixiang.yshop.module.product.service.shippingtemplatesfree.ShippingTemplatesFreeService;
import co.yixiang.yshop.module.product.service.shippingtemplatesregion.ShippingTemplatesRegionService;
import co.yixiang.yshop.module.product.service.storeproduct.AppStoreProductService;
import co.yixiang.yshop.module.product.service.storeproductreply.AppStoreProductReplyService;
import co.yixiang.yshop.module.product.service.storeproductstockwarning.StoreProductStockWarningService;
import co.yixiang.yshop.module.product.service.teamworkinfo.TeamworkInfoService;
import co.yixiang.yshop.module.shop.controller.app.vo.WriteOffDTO;
import co.yixiang.yshop.module.shop.dal.dataobject.shop.ShopDO;
import co.yixiang.yshop.module.shop.dal.dataobject.shopassistant.ShopAssistantDO;
import co.yixiang.yshop.module.shop.service.shop.ShopService;
import co.yixiang.yshop.module.shop.service.shopassistant.ShopAssistantService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.egzosn.pay.spring.boot.core.PayServiceManager;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
import static co.yixiang.yshop.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static co.yixiang.yshop.module.member.enums.BillDetailEnum.*;
import static co.yixiang.yshop.module.member.enums.ErrorCodeConstants.*;
import static co.yixiang.yshop.module.order.enums.ErrorCodeConstants.*;
import static co.yixiang.yshop.module.order.enums.OrderWriteOffStatusEnum.TO_BE_WRITTEN_OFF;
import static co.yixiang.yshop.module.order.enums.OrderWriteOffStatusEnum.WRITTEN_OFF;
import static co.yixiang.yshop.module.shop.enums.ErrorCodeConstants.SHOP_ASSISTANT_NOT_WRITE_OFF;
import static co.yixiang.yshop.module.system.enums.DictTypeConstants.*;

/**
 * 订单 Service 实现类
 *
 * @author yshop
 */
@Slf4j
@Service
@Validated
public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOrderDO> implements AppStoreOrderService {

    @Resource
    private StoreOrderMapper storeOrderMapper;
    @Resource
    private AppStoreCartService appStoreCartService;
    @Resource
    private AppUserAddressService appUserAddressService;
    @Resource
    private ShippingTemplatesService shippingTemplatesService;
    @Resource
    private ShippingTemplatesRegionService shippingTemplatesRegionService;
    @Resource
    private ShippingTemplatesFreeService shippingTemplatesFreeService;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private OrderRedisDAO orderRedisDAO;
    @Resource
    private AppStoreProductService appStoreProductService;
    @Resource
    private StoreOrderCartInfoService storeOrderCartInfoService;
    @Resource
    private StoreOrderStatusService storeOrderStatusService;
    @Resource
    private UserBillService billService;
    @Resource
    private AppStoreProductReplyService appStoreProductReplyService;
    @Resource
    private WeixinNoticeProducer weixinNoticeProducer;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private MerchantDetailsService merchantDetailsService;
    @Resource
    private AsyncStoreOrderService asyncStoreOrderService;
    @Resource
    private StoreOrderStatusMapper storeOrderStatusMapper;
    @Resource
    private AppCouponRelationService appCouponRelationService;
    @Resource
    private WxPayServiceUtils wxPayServiceUtils;
    @Resource
    private IntegralRuleContext integralRuleContext;
    @Resource
    private ShopAssistantService shopAssistantService;
    @Resource
    private ShopService shopService;
    @Resource
    private StoreOrderDetailService orderDetailService;
    @Resource
    private StoreOrderDetailMapper orderDetailMapper;
    @Resource
    private TeamworkInfoService teamworkInfoService;
    @Resource
    private RedissonUtil redissonUtil;
    @Resource
    private UserLevelConfigService userLevelConfigService;
    @Resource
    @Lazy
    private AppDistributorOrderFacade appDistributorOrderFacade;
    @Resource
    private AppCampaignInfoService appCampaignInfoService;
    @Resource
    private CampaignInfoService campaignInfoService;
    @Resource
    private CampaignDetailService campaignDetailService;
    @Resource
    private StoreOrderRuleService storeOrderRuleService;
    @Resource
    private StoreProductStockWarningService storeProductStockWarningService;

    private static final String LOCK_KEY = "cart:check:stock:lock";
    private static final String STOCK_LOCK_KEY = "cart:do:stock:lock";
    private static final String CREATE_ORDER_LOCK_KEY = "create_order:lock:";


    /**
     * 返回订单确认数据
     * 活动商品不可使用优惠券,不可使用会员折扣,不可使用积分抵扣
     *
     * @param uid 用户ID
     * @return ConfirmOrderVO
     */
    @Override
    public AppConfirmOrderVo confirmOrder(Long uid, AppConfirmOrderParam param) {
        param.setUseIntegral(Objects.isNull(param.getUseIntegral()) ? Boolean.FALSE : param.getUseIntegral());
        //查询购物车id，检查准备提交支付的商品
        Map<String, Object> cartGroup = appStoreCartService.getUserProductCartList(
                uid,
                param.getCartId(),
                OrderTypeEnum.CAMPAIGN_ORDER.getValue().equals(param.getOrderType()) ?
                        OrderTypeEnum.CAMPAIGN_ORDER.getValue() : OrderTypeEnum.NORMAL_ORDER.getValue());
        if (ObjectUtil.isNotEmpty(cartGroup.get(CartConstant.INVAILD_KEY))) {
            throw exception(INVALID_PRODUCT);
        }
        if (ObjectUtil.isEmpty(cartGroup.get(CartConstant.VAILD_KEY))) {
            throw exception(VALID_PRODUCT_EMPTY);
        }
        //获取用户信息
        MemberUserDO user = memberUserService.getUser(uid);

        boolean deduction = false;

        //获取默认地址
        UserAddressDO userAddress = getUserDefaultAddress(uid, param);

        List<AppStoreCartQueryVo> cartInfo = (List<AppStoreCartQueryVo>) cartGroup.get(CartConstant.VAILD_KEY);

        //计算订单价格信息
        PriceGroupDTO priceGroup;
        //活动订单，不能用优惠券，秒杀，拼团，限时折扣不能用积分，积分商城兑换需要使用积分
        if (OrderTypeEnum.CAMPAIGN_ORDER.getValue().equals(param.getOrderType())) {
            priceGroup = this.getOrderPriceGroupForCampaign(user, cartInfo, userAddress, param.getShippingType());
        } else {
            deduction = true;
            //正常下单，需要判断优惠券和使用积分抵扣的情况
            priceGroup = this.getOrderPriceGroup(user, cartInfo, userAddress, param.getCouponId(), param.getShippingType(), param.getUseIntegral());
        }
        //缓存订单
        CacheDTO cacheDTO = new CacheDTO();
        cacheDTO.setCartInfo(cartInfo);
        cacheDTO.setPriceGroup(priceGroup);
        //订单信息redis缓存一个小时
        String cacheKey = orderRedisDAO.set(cacheDTO, uid);

        return AppConfirmOrderVo.builder()
                .addressInfo(userAddress)
                .cartInfo(cartInfo)
                .priceGroup(priceGroup)
                .userInfo(UserConvert.INSTANCE.convert3(user))
                .orderKey(cacheKey)
                .deduction(deduction)
                .build();
    }

    private UserAddressDO getUserDefaultAddress(Long uid, AppConfirmOrderParam param) {
        return appUserAddressService.getOne(Wrappers.<UserAddressDO>lambdaQuery()
                .eq(UserAddressDO::getUid, uid)
                .eq(Objects.nonNull(param.getAddressId()) && param.getAddressId() != 0, UserAddressDO::getId, param.getAddressId())
                .orderByDesc(UserAddressDO::getIsDefault).last("limit 1"));
    }

    /**
     * 订单信息
     *
     * @param unique 唯一值或者单号
     * @param uid    用户id
     * @return YxStoreOrderQueryVo
     */
    @Override
    public AppStoreOrderQueryVo getOrderInfo(String unique, Long uid) {
        LambdaQueryWrapper<StoreOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.and(
                i -> i.eq(StoreOrderDO::getOrderId, unique).or().eq(StoreOrderDO::getUnique, unique).or()
                        .eq(StoreOrderDO::getExtendOrderId, unique));
        wrapper.eq(uid != null, StoreOrderDO::getUid, uid);
        return StoreOrderConvert.INSTANCE.convert01(storeOrderMapper.selectOne(wrapper));
    }


    /**
     * 计算订单价格
     *
     * @param uid   用户uid
     * @param key   订单缓存key
     * @param param 计算订单参数
     * @return map
     */
    @Override
    public Map<String, Object> computedOrder(Long uid, String key, AppComputeOrderParam param) {
        //检查订单
        Map<String, Object> map = this.check(uid, key);
        if (OrderLogEnum.EXTEND_ORDER.getValue().equals(map.get("status"))) {
            return map;
        }
        CacheDTO cacheDTO = orderRedisDAO.get(key, uid);
        if (ObjectUtil.isNull(cacheDTO)) {
            throw exception(ORDER_EXPIRED);
        }
        //获取用户信息
        MemberUserDO user = memberUserService.getUser(uid);

        //处理运费模板
        UserAddressDO userAddress = null;
        if (StrUtil.isNotBlank(param.getAddressId())) {
            userAddress = appUserAddressService.getById(param.getAddressId());
        }
        //计算订单价格信息
        PriceGroupDTO priceGroup;
        //活动订单，不能用优惠券，秒杀，拼团，限时折扣不能用积分，积分商城兑换需要使用积分
        if (Objects.nonNull(param.getCampaignDetailId())) {
            priceGroup = this.getOrderPriceGroupForCampaign(user, cacheDTO.getCartInfo(), userAddress, param.getShippingType());
        } else {
            //正常下单，需要判断优惠券和使用积分抵扣的情况
            priceGroup = this.getOrderPriceGroup(user, cacheDTO.getCartInfo(), userAddress, param.getCouponId(), param.getShippingType(), param.getUseIntegral());
        }

        BigDecimal payPrice = priceGroup.getTotalPrice();
        if (payPrice.compareTo(BigDecimal.ZERO) <= 0) {
            payPrice = BigDecimal.ZERO;
        }
        AppComputeVo appComputeVo = AppComputeVo.builder()
                .totalPrice(priceGroup.getCostPrice())
                .payPrice(payPrice)
                .payPostage(priceGroup.getStorePostage())
                .couponPrice(priceGroup.getCouponPrice())
                .deductionPrice(priceGroup.getDeductionPrice())
                .userIntegral(priceGroup.getIntegral())
                .payIntegral(priceGroup.getPayIntegral())
                .totalCouponPrice(priceGroup.getTotalCouponPrice())
                .vipDeductionAmount(priceGroup.getVipDeductionAmount())
                .build();

        map.put("result", appComputeVo);
        map.put("status", OrderLogEnum.NONE_ORDER.getValue());

        return map;
    }

    /**
     * 创建订单
     *
     * @param uid   用户uid
     * @param key   key
     * @param param param
     * @return YxStoreOrder
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Map<String, Object> createOrder(Long uid, String key, AppOrderParam param) {
        param.setUseIntegral(Objects.isNull(param.getUseIntegral()) ? Boolean.FALSE : param.getUseIntegral());
        AppComputeOrderParam appComputeOrderParam = AppComputeOrderParam.builder()
                .addressId(param.getAddressId())
                .couponId(param.getCouponId())
                .useIntegral(param.getUseIntegral())
                .shippingType(param.getShippingType())
                .campaignType(param.getCampaignType())
                .campaignDetailId(param.getCampaignDetailId())
                .build();
        Map<String, Object> map = this.computedOrder(uid, key, appComputeOrderParam);
        if (!OrderLogEnum.NONE_ORDER.getValue().equals(map.get("status"))) {
            return map;
        }
        AppComputeVo computeVo = (AppComputeVo) map.get("result");
        MemberUserDO user = memberUserService.getUser(uid);
        //是否使用了积分值
        BigDecimal payIntegral = computeVo.getPayIntegral();
        if (payIntegral != null && NumberUtil.sub(user.getIntegral(), payIntegral).compareTo(BigDecimal.ZERO) < 0) {
            throw exception(NOT_ENOUGH_INTEGRAL);
        }
        //用户地址校验
        UserAddressDO userAddress = verifyAndGetUserAddress(param);
        //门店相关校验
        verifyStore(param);

        AtomicInteger totalNum = new AtomicInteger(0);
        List<String> cartIds = new ArrayList<>();

        CacheDTO cacheDTO = orderRedisDAO.get(key, uid);
        List<AppStoreCartQueryVo> cartInfo = cacheDTO.getCartInfo();

        //对库存检查加锁
        RLock lock = redissonClient.getLock(LOCK_KEY);
        if (lock.tryLock()) {
            try {
                for (AppStoreCartQueryVo cart : cartInfo) {
                    //检测库存
                    appStoreCartService.checkProductStock(uid, cart.getProductId(), cart.getCartNum(), cart.getProductAttrUnique(),
                            param.getCampaignType() == null ? OrderTypeEnum.NORMAL_ORDER.getValue() :
                                    OrderTypeEnum.CAMPAIGN_ORDER.getValue());

                    cartIds.add(cart.getId().toString());
                    totalNum.addAndGet(cart.getCartNum());
                }
            } catch (Exception ex) {
                log.error("[checkProductStock][执行异常]", ex);
                throw exception(new ErrorCode(999999, ex.getMessage()));
            } finally {
                lock.unlock();
            }
        }

        //生成分布式唯一值
        String orderId = IdUtil.getSnowflake(0, 0).nextIdStr();
        //组合数据
        StoreOrderDO storeOrder = new StoreOrderDO();
        storeOrder.setUid(uid);
        storeOrder.setOrderId(orderId);
        storeOrder.setRealName(userAddress.getRealName());
        storeOrder.setUserPhone(userAddress.getPhone());
        String address = StrUtil.concat(true, userAddress.getProvince(), " ", userAddress.getCity(), " ", userAddress.getDistrict(), " ", userAddress.getDetail());
        storeOrder.setUserAddress(address);
        storeOrder.setCityId(userAddress.getCityId());
        storeOrder.setCartId(StrUtil.join(",", cartIds));
        storeOrder.setTotalNum(totalNum.get());
        storeOrder.setTotalPrice(computeVo.getTotalPrice());
        storeOrder.setTotalPostage(computeVo.getPayPostage());

        storeOrder.setCouponId(param.getCouponId() != null && param.getCouponId().compareTo(0L) > 0 ? param.getCouponId() : null);
        storeOrder.setCouponPrice(computeVo.getCouponPrice());
        storeOrder.setPayPrice(computeVo.getPayPrice());
        storeOrder.setPayPostage(computeVo.getPayPostage());
        storeOrder.setDeductionPrice(computeVo.getDeductionPrice());
        storeOrder.setVipDeductionAmount(computeVo.getVipDeductionAmount());
        storeOrder.setPaid(OrderInfoEnum.PAY_STATUS_UNPAID.getValue());
        //目前没传支付类型，默认类型微信
        //storeOrder.setPayType(PayTypeEnum.WEIXIN.getValue());
        storeOrder.setPayType("");

        storeOrder.setUseIntegral(computeVo.getUserIntegral());
        storeOrder.setPayIntegral(payIntegral);

        storeOrder.setTotalCouponPrice(computeVo.getTotalCouponPrice());
        storeOrder.setBackIntegral(BigDecimal.ZERO);
        //计算奖励积分
        BigDecimal gainIntegral = this.getGainIntegral(cartInfo);
        storeOrder.setGainIntegral(gainIntegral);
        storeOrder.setMark(param.getMark());
        storeOrder.setCost(cacheDTO.getPriceGroup().getCostPrice());
        storeOrder.setUnique(key);
        storeOrder.setShippingType(param.getShippingType());
        storeOrder.setStoreId(param.getStoreId());
        storeOrder.setCampaignDetailId(param.getCampaignDetailId());
        storeOrder.setCampaignType(param.getCampaignType());
        storeOrder.setTeamworkId(param.getTeamworkId());

        Integer autoOrderCancelTime = storeOrderRuleService.getOrderRuleCache().getAutoOrderCancelTime();
        storeOrder.setCancelTime(autoOrderCancelTime);

        storeOrder.setDistributorId(param.getDistributorId());
        boolean res = this.save(storeOrder);
        if (!res) {
            throw exception(ORDER_GEN_FAIL);
        }

        if (ObjectUtil.isNotNull(param.getCampaignType())) {
            // 减少活动库存，增加销量
            this.campaignStock(cartInfo);
        } else {
            // 减库存加销量
            this.deStockIncSale(cartInfo);
        }

        if (param.getCouponId() != null && param.getCouponId() != 0 && param.getCouponId() != -1) {
            // 使用优惠券
            appCouponRelationService.verificationCoupon(param.getCouponId());
        }


//        //支付成功，扣除用户积分
//        memberUserService.addIntegral(UserBillModifyDTO.builder()
//                .uid(uid)
//                .amount(NumberUtil.mul(-1, payIntegral))
//                .afterAmount(NumberUtil.sub(user.getIntegral(), payIntegral))
//                .linkId(storeOrder.getId())
//                .typeEnum(TYPE_12)
//                .categoryEnum(CATEGORY_2)
//                .pmType(BillEnum.PM_0)
//                .build());

        //保存购物车商品信息，异步执行
        storeOrderCartInfoService.saveCartInfo(storeOrder.getId(), storeOrder.getOrderId(), cartInfo);

        // 保存订单明细，异步执行
        orderDetailService.batchInsert(storeOrder.getId(), orderId, cartInfo, storeOrder);

        //购物车状态修改异步执行
        appStoreCartService.updateCartPayStatus(cartIds);

        //删除缓存
        orderRedisDAO.delete(key, uid);

        //增加状态
        storeOrderStatusService.create(uid, storeOrder.getId(), OrderLogEnum.CREATE_ORDER.getValue(),
                OrderLogEnum.CREATE_ORDER.getDesc());

        //加入延时队列，30分钟自动取消
        OrderDefaultRuleEnum ruleEnum = OrderDefaultRuleEnum.ORDER_OUTTIME_UNPAY;
        redissonUtil.delayedOffer(ruleEnum.getKey(), orderId, autoOrderCancelTime, ruleEnum.getUnit());

        OrderExtendDTO orderDTO = new OrderExtendDTO();
        orderDTO.setKey(key);
        orderDTO.setOrderId(storeOrder.getOrderId());
        map.put("status", OrderLogEnum.CREATE_ORDER_SUCCESS.getValue());
        map.put("result", orderDTO);
        map.put("createTime", storeOrder.getCreateTime());
        return map;
    }


    /**
     * 计算奖励的积分
     *
     * @param cartInfo cartInfo
     * @return double
     */
    private BigDecimal getGainIntegral(List<AppStoreCartQueryVo> cartInfo) {
        BigDecimal gainIntegral = BigDecimal.ZERO;
        for (AppStoreCartQueryVo cart : cartInfo) {
            if (cart.getCombinationId() > 0 || cart.getSeckillId() > 0 || cart.getBargainId() > 0) {
                continue;
            }
            BigDecimal cartInfoGainIntegral = BigDecimal.ZERO;
            Double gain = cart.getProductInfo().getGiveIntegral().doubleValue();
            if (gain > 0) {
                cartInfoGainIntegral = NumberUtil.round(NumberUtil.mul(cart.getCartNum(), gain), 2);
            }
            gainIntegral = NumberUtil.add(gainIntegral, cartInfoGainIntegral);
        }
        return gainIntegral;
    }


    /**
     * 用户地址校验
     *
     * @param param /
     */
    private UserAddressDO verifyAndGetUserAddress(AppOrderParam param) {
        if (ShippingTypeEnum.SHIPPING_TYPE_EXPRESS_DELIVERY.getValue().equals(param.getShippingType())) {
            if (StrUtil.isEmpty(param.getAddressId())) {
                throw exception(SELECT_ADDRESS);
            }
            UserAddressDO userAddress = appUserAddressService.getById(param.getAddressId());
            if (ObjectUtil.isNull(userAddress)) {
                throw exception(USER_ADDRESS_NOT_EXISTS);
            }
            return userAddress;
        } else if (ShippingTypeEnum.SHIPPING_TYPE_STORE.getValue().equals(param.getShippingType())) {
            return new UserAddressDO();
        } else {
            throw exception(PARAM_ERROR);
        }
    }


    /**
     * 门店相关校验
     *
     * @param param
     */
    private void verifyStore(AppOrderParam param) {
        if (param.getShippingType().equals(ShippingTypeEnum.SHIPPING_TYPE_STORE.getValue())) {
            if (Objects.isNull(param.getStoreId())) {
                throw exception(STORE_NOT_EXISTS);
            }
            ShopDO shop = shopService.getShop(param.getStoreId());
            if (Objects.isNull(shop)) {
                throw exception(STORE_NOT_EXISTS);
            }
        }
    }

    /**
     * 第三方支付查单
     *
     * @param uid   用户id
     * @param param 订单参数
     * @return 支付结果
     */
    public Boolean orderQuery(Long uid, AppPayParam param) {
        AppStoreOrderQueryVo orderInfo = getOrderInfo(param.getUni(), uid);
        if (ObjectUtil.isNull(orderInfo)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        if (orderInfo.getPayInfo() == null) {
            return false;
        }
        //paytype含有weixin就查微信订单
        //其他支付查单
        if (param.getPayType().contains(PayTypeEnum.WEIXIN.getType())) {
            //微信查单
            MerchantDetailsDO merchantDetailsDO = merchantDetailsService.getMerchantDetails("4");
            if (merchantDetailsDO == null) throw exception(ORDER_QUERY_NO_MERCHANT);
            PayInfo payInfo = JsonUtils.parseObject(orderInfo.getPayInfo(), PayInfo.class);
            if (payInfo == null) return false;
            if (payInfo.getTransaction_id() == null) return false;
            Map<String, String> map = PaymentApi.queryByTransactionId(merchantDetailsDO.getAppid(), merchantDetailsDO.getMchId(), merchantDetailsDO.getKeyPrivate(), payInfo.getTransaction_id());
            log.info("查询到微信订单信息：" + map.toString());
            return map.get("return_code").equals("SUCCESS") && map.get("result_code").equals("SUCCESS") && map.get("trade_state").equals("SUCCESS");
        } else return param.getPayType().contains(PayTypeEnum.YUE.getType());
    }

    /**
     * 第三方支付
     *
     * @param uid   用户id
     * @param param 订单参数
     * @return 支付结果
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Map<String, String> pay(Long uid, AppPayParam param) throws Exception {
//        AppStoreOrderQueryVo orderInfo = getOrderInfo(param.getUni(), uid);
//        if (ObjectUtil.isNull(orderInfo)) {
//            throw exception(STORE_ORDER_NOT_EXISTS);
//        }
//        if (orderInfo.getPaid().equals(OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())) {
//            throw exception(ORDER_PAY_FINISH);
//        }
//        //如果是小程序或APP 先获取用户openid
//        MemberUserDO userDO = memberUserService.getUser(uid);
//        if (PayTypeEnum.WEIXIN_APPLET.getCode().equals(param.getPayType())) {
//            //小程序支付需要openid
//            if (Objects.isNull(userDO.getRoutineOpenId())) throw exception(USER_NOT_BINDING_WX_APPLET);
//        }
//        //根据type得到支付商户配置
//        MerchantDetailsDO merchantDetailsDO = merchantDetailsService.getMerchantDetails(PayTypeEnum.toType(param.getPayType()).getValue());
//        PayContext payContext = new PayContext();
//        Map<String, String> packageParams = new HashMap<>();
//        if (param.getPayType().contains(PayTypeEnum.ALI.getValue())) {
//            aliPay(orderInfo, param, packageParams);
//            updatePayInfo(orderInfo, packageParams);
//        } else if (param.getPayType().contains(PayTypeEnum.WEIXIN.getValue())) {
//            packageParams = payContext.selectPayWay(PayTypeEnum.WEIXIN.getValue(),
//                    new PayParam(orderInfo.getOrderId(), orderInfo.getPayPrice(), userDO.getRoutineOpenId(), ServletUtils.getClientIP(),
//                            param.getPayType(), "Yshop商城", merchantDetailsDO, uid, PayOrderConstants.PAY_REDIRECT_URL));
//            updatePayInfo(orderInfo, packageParams);
//        } else if (param.getPayType().contains(PayTypeEnum.YUE.getValue())) {
//            yuePay(param.getUni(), uid);
//        }
//        // 异步统计
//        asyncStoreOrderService.orderData(uid);
//        return packageParams;
        return null;
    }

    @Override
    public void updatePayInfo(AppStoreOrderQueryVo orderInfo, Map<String, String> packageParams) {
        this.updatePayInfo(orderInfo, packageParams.get("out_trade_no"), packageParams.get("transaction_id"));
    }

    @Override
    public void updatePayInfo(AppStoreOrderQueryVo orderInfo, String outTradeNo, String transactionId) {
        PayInfo payInfo = new PayInfo();
        payInfo.setOrderId(orderInfo.getOrderId());
        payInfo.setOut_trade_no(outTradeNo);
        payInfo.setTransaction_id(transactionId);
        //更新订单支付信息
        LambdaQueryWrapper<StoreOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreOrderDO::getOrderId, orderInfo.getOrderId());
        StoreOrderDO storeOrder = new StoreOrderDO();
        storeOrder.setPayInfo(JsonUtils.toJsonString(payInfo));
        this.update(storeOrder, wrapper);
    }

    private void aliPay(AppStoreOrderQueryVo orderInfo, AppPayParam param, Map<String, String> packageParams) {
//        if (PayTypeEnum.ALI_H5.getValue().equals(param.getPayType())) {
//            MerchantPayOrder payOrder = new MerchantPayOrder("1", "WAP", "支付宝H5商品购买",
//                    "h5商品购买", orderInfo.getPayPrice(), param.getUni() + "-" + RandomStringUtil.getRandomCode(3, 0) + "H5");
//            String str = manager.toPay(payOrder);
//            log.info(str);
//            // packageParams.put("data",manager.toPay(payOrder));
//        } else if (PayTypeEnum.ALI_APP.getValue().equals(param.getFrom())) {
//            MerchantPayOrder payOrder = new MerchantPayOrder("1", "APP", "支付宝APP商品购买",
//                    "支付宝APP5商品购买", orderInfo.getPayPrice(), param.getUni() + "-" + RandomStringUtil.getRandomCode(3, 0) + "APP");
//            //  packageParams.put("data",manager.app(payOrder));
//        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
//        log.info("进入微信支付回调");
//        String xmlMsg = HttpKit.readData(request);
//        log.info("微信回调通知信息" + xmlMsg);
//        if (StringUtils.isEmpty(xmlMsg)) {
//            log.info("回调接口内容为空！");
//            return;
//        }
//        Map<String, String> resultMap = PaymentKit.xmlToMap(xmlMsg);
//        //验签
//        MerchantDetailsDO merchantDetailsDO = merchantDetailsService.getMerchantDetails("4");
//        boolean validSignResult = PaymentKit.verifyNotify(resultMap, merchantDetailsDO.getKeyPrivate());
//        log.info("验签结果{}", validSignResult);
//        if (!MapUtils.isEmpty(resultMap)) {
//            if (resultMap.get("return_code").equals("SUCCESS")) {
//                String orderNo = resultMap.get("out_trade_no");
//                log.info("回调out_trade_no=" + orderNo);
//                log.info("回调transaction_id=" + resultMap.get("transaction_id"));
//                //截取订单编号
//                String[] split = orderNo.split("-");
//                if (split.length > 0) {
//                    String orderFormId = split[0];
//                    if (!StringUtils.isEmpty(orderFormId)) {
//                        LambdaQueryWrapper<StoreOrderDO> wrapper = new LambdaQueryWrapper<>();
//                        wrapper.and(
//                                i -> i.eq(StoreOrderDO::getOrderId, orderFormId).or().eq(StoreOrderDO::getUnique, orderFormId).or()
//                                        .eq(StoreOrderDO::getExtendOrderId, orderFormId));
//                        StoreOrderDO orderInfo = storeOrderMapper.selectOne(wrapper);
//                        if (orderInfo == null) {
//                            log.info("订单已经取消或删除");
//                            response.getWriter().write(PayOrderConstants.WEINXIN_RESULT);
//                        }
//                        //判断订单装态，如果已支付说明为重复回调，丢弃
//                        assert orderInfo != null;
//                        if (OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(orderInfo.getPaid())) {
//                            log.info("订单【{}】已经支付完成", orderInfo.getOrderId());
//                            response.getWriter().write(PayOrderConstants.WEINXIN_RESULT);
//                        }
//                        //判断金额是否一致
//                        BigDecimal totalFee = new BigDecimal(resultMap.get("total_fee"));
//                        PayInfo payInfo = JsonUtils.parseObject(orderInfo.getPayInfo(), PayInfo.class);
//                        log.info(totalFee + ":" + orderInfo.getPayPrice().multiply(BigDecimal.valueOf(100)));
//                        if (!(totalFee.compareTo(orderInfo.getPayPrice().multiply(BigDecimal.valueOf(100))) == 0)) {
//                            log.info("支付金额与订单金额不一致，请查询相关订单信息与客户确认！");
////                            throw exception(STORE_ORDER_PAY_FEE_ERROR);
//                            //最简单做法 不一致退还支付金额,订单装态不变
//                            assert payInfo != null;
//                            // 微信 根据不同付款方式进行退款 小程序 app
//                            if (payInfo.getOut_trade_no().contains("APPLET")) {
//                                merchantDetailsDO = merchantDetailsService.getMerchantDetails(PayTypeEnum.WEIXIN_APPLET.getCode());
//                            } else {
//                                //app 和h5 appId目前一样
//                                merchantDetailsDO = merchantDetailsService.getMerchantDetails(PayTypeEnum.WEIXIN_H5.getCode());
//                            }
//                            Map map = wxPayServiceUtils.refund(resultMap.get("transaction_id"), payInfo.getOut_refund_no(), orderInfo.getTotalPrice(), totalFee, merchantDetailsDO);
//                            log.info("退款详情:" + map);
//                            response.getWriter().write(PayOrderConstants.WEINXIN_RESULT);
//                        }
//                        //更新订单状态，与支付单据信息
//
//                        assert payInfo != null;
//                        payInfo.setTransaction_id(resultMap.get("transaction_id"));
//                        LambdaQueryWrapper<StoreOrderDO> updateWrapper = new LambdaQueryWrapper<>();
//                        updateWrapper.eq(StoreOrderDO::getOrderId, orderFormId);
//                        StoreOrderDO storeOrder = new StoreOrderDO();
//                        storeOrder.setPayInfo(JsonUtils.toJsonString(payInfo));
//                        this.update(storeOrder, updateWrapper);
//                        //支付完成回调的逻辑 修改订单装态等
//                        this.paySuccess(orderFormId, PayTypeEnum.WEIXIN.getValue());
//                    }
//                }
//            }
//        } else {
//            log.info("map为null");
//        }
//        log.info("微信小程序支付回调成功");
//        response.getWriter().write(PayOrderConstants.WEINXIN_RESULT);
    }

    @Override
    public void wxPayNotify(String orderId, Map<String, String> resultMap) throws Exception {
        MerchantDetailsDO merchantDetailsDO;
        LambdaQueryWrapper<StoreOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.and(
                i -> i.eq(StoreOrderDO::getOrderId, orderId).or().eq(StoreOrderDO::getUnique, orderId).or()
                        .eq(StoreOrderDO::getExtendOrderId, orderId));
        StoreOrderDO orderInfo = storeOrderMapper.selectOne(wrapper);
        if (orderInfo == null) {
            log.info("订单已经取消或删除");
            return;
        }
        //判断订单装态，如果已支付说明为重复回调，丢弃
        assert orderInfo != null;
        if (OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(orderInfo.getPaid())) {
            log.info("订单【{}】已经支付完成", orderInfo.getOrderId());
            return;
        }
        //判断金额是否一致
        BigDecimal totalFee = new BigDecimal(resultMap.get("total_fee"));
        PayInfo payInfo = JsonUtils.parseObject(orderInfo.getPayInfo(), PayInfo.class);
        log.info(totalFee + ":" + orderInfo.getPayPrice().multiply(BigDecimal.valueOf(100)));
        if (!(totalFee.compareTo(orderInfo.getPayPrice().multiply(BigDecimal.valueOf(100))) == 0)) {
            log.info("支付金额与订单金额不一致，请查询相关订单信息与客户确认！");
            //最简单做法 不一致退还支付金额,订单装态不变
            assert payInfo != null;
            // 微信 根据不同付款方式进行退款 小程序 app
            if (payInfo.getOut_trade_no().contains("APPLET")) {
                merchantDetailsDO = merchantDetailsService.getMerchantByType(PayTypeEnum.WEIXIN_APPLET.getType());
            } else {
                //app 和h5 appId目前一样
                merchantDetailsDO = merchantDetailsService.getMerchantByType(PayTypeEnum.WEIXIN_H5.getType());
            }
            Map map = wxPayServiceUtils.refund(resultMap.get("transaction_id"), payInfo.getOut_refund_no(), orderInfo.getTotalPrice(), totalFee, merchantDetailsDO);
            log.info("退款详情:" + map);
            return;
        }
        //更新订单状态，与支付单据信息

        assert payInfo != null;
        payInfo.setTransaction_id(resultMap.get("transaction_id"));
        LambdaQueryWrapper<StoreOrderDO> updateWrapper = new LambdaQueryWrapper<>();
        updateWrapper.eq(StoreOrderDO::getOrderId, orderId);

        StoreOrderDO storeOrder = new StoreOrderDO();
        storeOrder.setPayInfo(JsonUtils.toJsonString(payInfo));
        this.update(storeOrder, updateWrapper);
        //支付完成回调的逻辑 修改订单装态等

        String helpPay = null;
        Long uid = null;
        /*
          附加参数，这里暂时只简单处理代付参数,是否代付 ，付款者uid
         */
        String attach = resultMap.get("attach");
        if (attach != null) {
            String[] split = attach.split(",");
            helpPay = split[0];
            uid = Long.valueOf(split[1]);
        }
        this.paySuccess(orderId, PayTypeEnum.WEIXIN.getType(), uid, "1".equals(helpPay));
    }

    /**
     * 余额支付
     *
     * @param orderId 订单号
     * @param uid     用户id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void yuePay(String orderId, Long uid, Boolean helpPay) {
        AppStoreOrderQueryVo orderInfo = getOrderInfo(orderId, null);
        if (ObjectUtil.isNull(orderInfo)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }

        if (OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(orderInfo.getPaid())) {
            throw exception(ORDER_PAY_FINISH);
        }

        AppUserQueryVo userInfo = memberUserService.getAppUser(uid);

        if (userInfo.getNowMoney().compareTo(orderInfo.getPayPrice()) < 0) {
            throw exception(PAY_YUE_NOT);
        }
        //扣除余额
        memberUserService.decPrice(uid, orderInfo.getPayPrice());

        //支付成功后处理
        this.paySuccess(orderInfo.getOrderId(), PayTypeEnum.YUE.getType(), uid, helpPay);
    }


    /**
     * 支付成功后操作
     *
     * @param orderId 订单号
     * @param payType 支付方式
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void paySuccess(String orderId, String payType, Long targetUid, Boolean helpPay) {
        AppStoreOrderQueryVo orderInfo = getOrderInfo(orderId, null);

        StoreOrderDO storeOrder = new StoreOrderDO();
        OrderLogEnum orderLogEnum = OrderLogEnum.PAY_ORDER_SUCCESS;
        BillTitleEnum billTitle = BillTitleEnum.BUY_GOODS;
        if (helpPay) {
            storeOrder.setHelpUid(targetUid);
            orderLogEnum = OrderLogEnum.HELP_PAY_ORDER_SUCCESS;
            billTitle = BillTitleEnum.HELP_PAY_GOODS;
        }
        storeOrder.setPaid(OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue());
        storeOrder.setPayType(payType);
        storeOrder.setPayTime(LocalDateTime.now());
        //自提订单 生成核销码
        if (Objects.equals(orderInfo.getShippingType(), ShippingTypeEnum.SHIPPING_TYPE_STORE.getValue())) {
            storeOrder.setWriteOffCode(RandomUtil.randomNumbers(10));
        }
        // 拼团订单进入待成团状态，加入拼团信息
        if (ObjectUtil.isNotNull(orderInfo.getCampaignType()) &&
                CampaignTypeEnum.TEAMWORK.getValue().equals(orderInfo.getCampaignType())) {
            TeamworkInfoCreateVO createReqVO = new TeamworkInfoCreateVO();
            BeanUtils.copyProperties(orderInfo, createReqVO);
            log.info("成团信息：{}", JSONObject.toJSONString(createReqVO));
            Long teamworkId = teamworkInfoService.toTeamwork(createReqVO);
            storeOrder.setTeamworkId(teamworkId);
            Integer teamworkState = teamworkInfoService.getTeamworkState(teamworkId);
            // 拼团成功后，修改其他订单为发货，未支付订单为已取消
            if (TeamworkStateEnum.SUCCESS.getValue().equals(teamworkState)) {
                storeOrder.setStatus(OrderInfoEnum.STATUS_DEFAULT.getValue());
                this.update(new LambdaUpdateWrapper<StoreOrderDO>()
                        .set(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_DEFAULT.getValue())
                        .eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getTeamworkId, teamworkId));
                this.update(new LambdaUpdateWrapper<StoreOrderDO>()
                        .set(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_CANCEL.getValue())
                        .eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_UNPAID.getValue())
                        .eq(StoreOrderDO::getTeamworkId, teamworkId));
            } else {
                storeOrder.setStatus(OrderStatusEnum.STATUS_WAIT_FOR_GROUP.getValue());
            }
        }
        //积分活动中，如果当前活动库存剩余为0，则结束该活动
        if (ObjectUtil.isNotNull(orderInfo.getCampaignType()) &&
                CampaignTypeEnum.INTEGRAL.getValue().equals(orderInfo.getCampaignType())) {
            Long campaignDetailId = orderInfo.getCampaignDetailId();
            int stock = campaignDetailService.campaignStockRemain(campaignDetailId);
            if (stock == 0) {
                Long campaignId = campaignDetailService.getCampaignId(campaignDetailId);
                campaignInfoService.closeCampaignInfo(campaignId);
                log.info("-------------库存为0，活动详情id:{} 对应的活动结束", campaignDetailId);
            }
        }
        //更新订单
        this.update(storeOrder, Wrappers.<StoreOrderDO>lambdaQuery()
                .eq(StoreOrderDO::getOrderId, orderId)
        );
        MemberUserDO userInfo = memberUserService.getUser(targetUid);
        //扣除积分
        BigDecimal payIntegral = orderInfo.getPayIntegral();
        if (payIntegral != null && payIntegral.compareTo(BigDecimal.ZERO) > 0) {
            this.payIntegral(targetUid, userInfo.getIntegral(), payIntegral, orderInfo.getId(), orderInfo.getCampaignType());
        }
        //增加用户购买次数
        memberUserService.incPayCount(orderInfo.getUid());
        //增加用户消费金额
        memberUserService.addConsumeAmount(orderInfo.getUid(),orderInfo.getPayPrice());
        //增加状态
        storeOrderStatusService.create(
                orderInfo.getUid(), orderInfo.getId(), orderLogEnum.getValue(), orderLogEnum.getDesc()
        );

        //增加流水
        String payTypeMsg = PayTypeEnum.WEIXIN.getDesc();
        if (PayTypeEnum.YUE.getType().equals(payType)) {
            payTypeMsg = PayTypeEnum.YUE.getDesc();
        }
        billService.expend(
                targetUid,
                billTitle.getValue(),
                PayTypeEnum.YUE.getType().equals(payType) ?
                        BillDetailEnum.CATEGORY_1.getValue() : BillDetailEnum.CATEGORY_3.getValue(),
                BillDetailEnum.TYPE_3.getValue(),
                orderInfo.getPayPrice().doubleValue(),
                userInfo.getNowMoney().doubleValue(),
                payTypeMsg + orderInfo.getPayPrice() + "元" + billTitle
        );

        //公众号订单通知管理员
        weixinNoticeProducer.sendNoticeMessage( new WeixinNoticeMessage()
                .setOrderId(orderInfo.getOrderId())
                .setTempkey(WechatTempateEnum.NEW_ORDER_NOTICE.getValue())
                .setType(WechatTempateEnum.TEMPLATES.getValue()));


        // 保存分销详情
        appDistributorOrderFacade.createDistributorOrder(new DistributorOrderDTO().setOid(orderInfo.getId()));
    }

    private void payIntegral(Long uid, BigDecimal userIntegral, BigDecimal payIntegral, Long orderId, Integer campaignType) {
        //支付成功，扣除用户积分
        memberUserService.addIntegral(UserBillModifyDTO.builder()
                .uid(uid)
                .amount(NumberUtil.mul(-1, payIntegral))
                .afterAmount(NumberUtil.sub(userIntegral, payIntegral))
                .linkId(orderId)
                .typeEnum(campaignType != null && campaignType.equals(CampaignTypeEnum.INTEGRAL.getValue()) ?
                        TYPE_15 : TYPE_12)
                .categoryEnum(CATEGORY_2)
                .pmType(BillEnum.PM_0)
                .build());

    }

    /**
     * 减库存增加销量
     *
     * @param cartInfo 购物车
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deStockIncSale(List<AppStoreCartQueryVo> cartInfo) {
        log.info("========减库存增加销量start=========");
        //对库存加锁
        RLock lock = redissonClient.getLock(STOCK_LOCK_KEY);
        if (lock.tryLock()) {
            try {
                for (AppStoreCartQueryVo storeCartVO : cartInfo) {
                    appStoreProductService.decProductStock(storeCartVO.getCartNum(), storeCartVO.getProductId(),
                            storeCartVO.getProductAttrUnique(), 0L, "");
                    //库存预警检测
                    storeProductStockWarningService.checkStockWarning(storeCartVO.getProductId(),storeCartVO.getProductAttrUnique());
                }
            } catch (Exception ex) {
                log.error("[deStockIncSale][执行异常]", ex);
                throw exception(new ErrorCode(999999, ex.getMessage()));
            } finally {
                lock.unlock();
            }
        }
    }


    /**
     * 订单列表
     *
     * @param uid   用户id
     * @param type  OrderStatusEnum
     * @param page  page
     * @param limit limit
     * @return list
     */
    @Override
    public List<AppStoreOrderQueryVo> orderList(Long uid, int type, int page, int limit) {
        LambdaQueryWrapper<StoreOrderDO> wrapper = new LambdaQueryWrapper<>();
        if (uid != null) {
            wrapper.eq(StoreOrderDO::getUid, uid);
        }
        wrapper.orderByDesc(StoreOrderDO::getId);


        switch (OrderStatusEnum.toType(type)) {
            case STATUS_ALL:
                break;
            case STATUS_PAY:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue());
                break;
            //未支付
            case STATUS_WAIT_PAY:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_UNPAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_DEFAULT.getValue());
                break;
            // 待成团
            case STATUS_WAIT_FOR_GROUP:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_WAIT_GROUP.getValue());
                break;
            //待发货
            case STATUS_WAIT_SEND:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_DEFAULT.getValue());
                break;
            //待收货
            case STATUS_WAIT_RECEIVED:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_WAIT_RECEIVED.getValue());
                break;
            //待评价
            case STATUS_WAIT_EVALUATE:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_RECEIVED.getValue());
                break;
            //已完成
            case STATUS_FINISH:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue())
                        .eq(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_FINISHED.getValue());
                break;
            //退款中
            case STATUS_MINUS_BEING_REFUND:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_BEING_REFUNDED.getValue());
                break;
            //已退款
            case STATUS_MINUS_REFUNDED:
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_UNPAID.getValue())
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue());
                break;
            //退款
            case STATUS_MINUS_REFUND:
                String[] strs = {"1", "2"};
                wrapper.eq(StoreOrderDO::getPaid, OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue())
                        .in(StoreOrderDO::getRefundStatus, Arrays.asList(strs));
                break;
            default:
        }

        Page<StoreOrderDO> pageModel = new Page<>(page, limit);
        IPage<StoreOrderDO> pageList = storeOrderMapper.selectPage(pageModel, wrapper);
        List<AppStoreOrderQueryVo> list = StoreOrderConvert.INSTANCE.convertList01(pageList.getRecords());
        return list.stream().map(this::handleOrder).collect(Collectors.toList());
    }


    /**
     * 处理订单返回的状态
     *
     * @param order order
     * @return YxStoreOrderQueryVo
     */
    @Override
    public AppStoreOrderQueryVo handleOrder(AppStoreOrderQueryVo order) {
        LambdaQueryWrapper<StoreOrderCartInfoDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StoreOrderCartInfoDO::getOid, order.getId());
        List<StoreOrderCartInfoDO> cartInfos = storeOrderCartInfoService.list(wrapper);
        // 查询是否发货 为后面待发货提前计算时间
        StoreOrderStatusDO orderStatusDO =
                storeOrderStatusMapper.selectOne(new LambdaQueryWrapper<StoreOrderStatusDO>()
                        .eq(StoreOrderStatusDO::getOid, order.getId())
                        .eq(StoreOrderStatusDO::getChangeType, OrderLogEnum.DELIVERY_GOODS.getValue())
                        .orderByDesc(StoreOrderStatusDO::getId).last("limit 1"));
        if (Objects.nonNull(orderStatusDO)) {
            order.setDeliveryTime(orderStatusDO.getChangeTime());
        }
        List<AppStoreCartQueryVo> cartInfo = cartInfos.stream()
                .map(cart -> {
                    AppStoreCartQueryVo cartQueryVo = JSON.parseObject(cart.getCartInfo(), AppStoreCartQueryVo.class);
                    cartQueryVo.setUnique(cart.getUnique());
                    cartQueryVo.setIsReply(appStoreProductReplyService.replyCount(cart.getUnique()));
                    return cartQueryVo;
                })
                .collect(Collectors.toList());
        List<String> productAttrUniques =
                cartInfo.stream().map(AppStoreCartQueryVo::getProductAttrUnique).collect(Collectors.toList());
        // 查询订单明细
        List<AppOrderDetailVO> orderDetailDOS =
                orderDetailMapper.getOrderDetails(productAttrUniques, order.getId());
        if (CollectionUtils.isNotEmpty(orderDetailDOS)) {
            Map<String, List<AppOrderDetailVO>> orderDetailMap =
                    orderDetailDOS.stream().collect(Collectors.groupingBy(AppOrderDetailVO::getProductAttrUnique));
            // 合并同规格同售后状态的订单详情，同规格不同售后状态的分开显示
            List<AppStoreCartQueryVo> addCartInfoList = new ArrayList<>();
            for (AppStoreCartQueryVo cartQueryVo : cartInfo) {
                List<AppOrderDetailVO> detailDOS = orderDetailMap.get(cartQueryVo.getProductAttrUnique());
                if (CollectionUtils.isEmpty(detailDOS)) continue;
                Map<Integer, List<AppOrderDetailVO>> orderDetailStatusMap =
                        detailDOS.stream().collect(Collectors.groupingBy(AppOrderDetailVO::getState));
                if (orderDetailStatusMap.keySet().size() > 1) {
                    for (Integer state : orderDetailStatusMap.keySet()) {
                        if (OrderDetailStateEnum.IN_AFTER_SALES.getValue().equals(state) ||
                                OrderDetailStateEnum.AFTER_SALES_COMPLETE.getValue().equals(state)) {
                            AppStoreCartQueryVo cloneCartQueryVo = SerializationUtils.clone(cartQueryVo);
                            cloneCartQueryVo.setOrderDetailState(state);
                            cloneCartQueryVo.setCartNum(orderDetailStatusMap.get(state).size());
                            addCartInfoList.add(cloneCartQueryVo);
                        } else {
                            cartQueryVo.setCartNum(orderDetailStatusMap.get(state).size());
                            cartQueryVo.setOrderDetailState(state);
                            addCartInfoList.add(cartQueryVo);
                        }
                    }
                } else {
                    cartQueryVo.setOrderDetailState(orderDetailStatusMap.keySet().iterator().next());
                    addCartInfoList.add(cartQueryVo);
                }
            }
            cartInfo = addCartInfoList;
        }
        // 按购物车排序
        cartInfo = cartInfo.stream().sorted(Comparator.comparingLong(AppStoreCartQueryVo::getId))
                .collect(Collectors.toList());
        order.setCartInfo(cartInfo);

        StatusDTO statusDTO = new StatusDTO();
        if (OrderStatusEnum.STATUS_WAIT_PAY.getValue().equals(order.getPaid()) && !OrderInfoEnum.STATUS_CANCEL.getValue().equals(order.getStatus())) {
            //计算未支付到自动取消订 时间
//            int offset = Integer.parseInt(String.valueOf(ShopConstants.ORDER_OUTTIME_UNPAY));
            int offset = order.getCancelTime();
            log.info("time" + order.getCreateTime());
            LocalDateTime time = order.getCreateTime().plusMinutes(offset);
            statusDTO.set_class("nobuy");
            statusDTO.set_msg(StrUtil.format("请在{}前完成支付", time.format(DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND))));
            statusDTO.set_type(OrderStatusEnum.STATUS_WAIT_PAY.getValue().toString());
            long remainTime = time.toEpochSecond(ZoneOffset.ofHours(8)) * 1000 - System.currentTimeMillis();
            statusDTO.set_payRemainTime(String.valueOf(remainTime));
            statusDTO.set_title(OrderStatusEnum.STATUS_WAIT_PAY.getDesc());
        } else if (OrderInfoEnum.REFUND_STATUS_BEING_REFUNDED.getValue().equals(order.getRefundStatus())) {
            statusDTO.set_class("state-sqtk");
            statusDTO.set_msg("商家审核中,请耐心等待");
            statusDTO.set_type(OrderInfoEnum.STATUS_APPLY_REFUND.getValue().toString());
            statusDTO.set_title("申请退款中");
        } else if (OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue().equals(order.getRefundStatus())) {
            statusDTO.set_class("state-sqtk");
            statusDTO.set_msg("已为您退款,感谢您的支持");
            statusDTO.set_type(OrderInfoEnum.STATUS_REFUND_SUCCESS.getValue().toString());
            statusDTO.set_title(OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getDesc());
        } else if (OrderInfoEnum.STATUS_WAIT_GROUP.getValue().equals(order.getStatus())) {
            if (ObjectUtil.isNotNull(order.getCampaignDetailId())) {
                // 查询成团剩余时间
                Long endTime = teamworkInfoService.teamworkEndTime(order.getTeamworkId());
                long remainder = endTime - System.currentTimeMillis();
                int hours = (int) (remainder / (1000 * 60 * 60));
                int minutes = (int) ((remainder % (1000 * 60 * 60)) / (1000 * 60));
                statusDTO.set_class("state-dct");
                statusDTO.set_groupRemainTime(endTime);
                statusDTO.set_msg("还剩" + hours + "小时" + minutes + "分自动关闭");
                statusDTO.set_type("6");
                statusDTO.set_title("等待成团");
            }

        } else if (OrderInfoEnum.STATUS_WAIT_RECEIVED.getValue().equals(order.getStatus())) {
            //计算发货到自动收货 时间
//            int offset = Integer.parseInt(String.valueOf(ShopConstants.ORDER_OUTTIME_UNCONFIRM));
            int offset = storeOrderRuleService.getOrderRuleCache().getAutoOrderTakeTime();
            log.info("time" + order.getDeliveryTime());
            LocalDateTime time = order.getDeliveryTime().plusDays(offset);
            statusDTO.set_class("state-ysh");
            statusDTO.set_type("2");
            statusDTO.set_title(OrderInfoEnum.STATUS_WAIT_RECEIVED.getDesc());
            statusDTO.set_msg("待收货," + StrUtil.format("{}自动确认", time.format(DateTimeFormatter.ofPattern(FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND))));
            long remainTime = time.toEpochSecond(ZoneOffset.ofHours(8)) * 1000 - System.currentTimeMillis();
            statusDTO.set_payRemainTime(String.valueOf(remainTime));
        } else if (OrderInfoEnum.STATUS_RECEIVED.getValue().equals(order.getStatus())) {
            statusDTO.set_class("state-ypj");
            statusDTO.set_msg("已收货,快去评价一下吧");
            statusDTO.set_type("3");
            statusDTO.set_title("待评价");
        } else if (OrderInfoEnum.STATUS_FINISHED.getValue().equals(order.getStatus())) {
            statusDTO.set_class("state-ytk");
            statusDTO.set_msg("交易完成,感谢您的支持");
            statusDTO.set_type("4");
            statusDTO.set_title("交易完成");
        } else if (OrderInfoEnum.STATUS_CANCEL.getValue().equals(order.getStatus())) {
            statusDTO.set_class("state-yqx");
            statusDTO.set_msg("交易取消,感谢您的支持");
            statusDTO.set_type("5");
            statusDTO.set_title("已取消");
        } else if (OrderInfoEnum.STATUS_DEFAULT.getValue().equals(order.getStatus())
                && !OrderStatusEnum.STATUS_WAIT_PAY.getValue().equals(order.getPaid())) {
            statusDTO.set_class("state-dfh");
            statusDTO.set_msg("等待商家发货");
            statusDTO.set_type("8");
            statusDTO.set_title("待发货");
        } else if (OrderInfoEnum.STATUS_GROUP_FAILURE.getValue().equals(order.getStatus())) {
            statusDTO.set_class("state-ctsb");
            statusDTO.set_msg("未能按时成团");
            statusDTO.set_type("7");
            statusDTO.set_title("成团失败");
        }

        if (PayTypeEnum.WEIXIN.getType().equals(order.getPayType())) {
            statusDTO.set_payType(PayTypeEnum.WEIXIN.getDesc());
        } else if (PayTypeEnum.YUE.getType().equals(order.getPayType())) {
            statusDTO.set_payType(PayTypeEnum.YUE.getDesc());
        } else {
            statusDTO.set_payType(PayTypeEnum.INTEGRAL.getDesc());
        }

        order.set_status(statusDTO);

        Long orderIsReply = cartInfo.stream().map(AppStoreCartQueryVo::getIsReply).reduce(0L, Long::sum);
        order.setIsReply(orderIsReply);
        return order;
    }

    /**
     * 订单确认收货
     *
     * @param orderId 单号
     * @param uid     uid
     */
    @Override
    public void takeOrder(String orderId, Long uid,String type) {
        AppStoreOrderQueryVo order = this.getOrderInfo(orderId, uid);
        if (ObjectUtil.isNull(order)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        order = handleOrder(order);
        if(StrUtil.isNotBlank(type) && type.equals("user")){
            if (!OrderStatusEnum.STATUS_WAIT_RECEIVED.getValue().toString().equals(order.get_status().get_type())) {
                throw exception(ORDER_STATUS_ERROR);
            }
        }

        StoreOrderDO storeOrder = new StoreOrderDO();
        storeOrder.setStatus(OrderInfoEnum.STATUS_RECEIVED.getValue());
        storeOrder.setId(order.getId());
        storeOrder.setCompleteTime(LocalDateTime.now());
        this.updateById(storeOrder);

        //增加状态
        storeOrderStatusService.create(order.getUid(), order.getId(), OrderLogEnum.TAKE_ORDER_DELIVERY.getValue(), OrderLogEnum.TAKE_ORDER_DELIVERY.getDesc());

        // 结算返佣
        appDistributorOrderFacade.updateDistributorOrder(new DistributorOrderDTO().setOid(order.getId()).setDistributorSettlementType(DistributorSettlementTypeEnum.CONFIRM.getType()));

        //关闭售后天数
//        Integer closeAfterSaleDays = Integer.valueOf(DictFrameworkUtils.parseDictDataValue(CLOSE_AFTER_SALE_DAYS, CLOSE_AFTER_SALE_DAYS));
        Integer closeAfterSaleDays = storeOrderRuleService.getOrderRuleCache().getNoAfterSalesTime();
//        redissonUtil.delayedOffer(ShopConstants.CLOSE_AFTER_SALE_KEY,orderId, closeAfterSaleDays, TimeUnit.DAYS);
        OrderDefaultRuleEnum orderMaxCanAfterSaleTimeEnum = OrderDefaultRuleEnum.ORDER_MAX_CAN_AFTER_SALE_TIME;
        redissonUtil.delayedOffer(orderMaxCanAfterSaleTimeEnum.getKey(), order.getId(), closeAfterSaleDays, orderMaxCanAfterSaleTimeEnum.getUnit());

    }

    @Override
    public boolean canComment(Long id) {
        StoreOrderDO storeOrderDO = storeOrderMapper.selectById(id);
        if (storeOrderDO == null) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        LocalDateTime completeTime = storeOrderDO.getCompleteTime();
        if (completeTime == null) {
            return false;
        }
        return !LocalDateTime.now().isAfter(completeTime.plusDays(storeOrderRuleService.getOrderRuleCache().getOrderNoCommentTime()));
    }

    /**
     * 订单评价
     *
     * @param uid          用户ID
     * @param unique       订单orderCart唯一值
     * @param comment      评论内容
     * @param pics         图片
     * @param productScore 评分
     * @param serviceScore 评分
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void orderComment(Long uid, String unique, String comment, String pics, String productScore,
                             String serviceScore) {
        StoreOrderCartInfoDO orderCartInfo = storeOrderCartInfoService
                .getOne(Wrappers.<StoreOrderCartInfoDO>lambdaQuery()
                        .eq(StoreOrderCartInfoDO::getUnique, unique));

        if (ObjectUtil.isEmpty(orderCartInfo)) {
            throw exception(COMMENT_PRODUCT_NOT_EXISTS);
        }

        long count = appStoreProductReplyService.count(Wrappers.<StoreProductReplyDO>lambdaQuery()
                .eq(StoreProductReplyDO::getOid, orderCartInfo.getOid())
                .eq(StoreProductReplyDO::getProductId, orderCartInfo.getProductId())
                .eq(StoreProductReplyDO::getUnique, unique));
        if (count > 0) {
            throw exception(COMMENT_PRODUCT_IN_EXISTS);
        }


        StoreProductReplyDO storeProductReply = StoreProductReplyDO.builder()
                .uid(uid)
                .oid(orderCartInfo.getOid())
                .productId(orderCartInfo.getProductId())
                .productScore(Integer.valueOf(productScore))
                .serviceScore(Integer.valueOf(serviceScore))
                .comment(comment)
                .pics(pics)
                .unique(unique)
                .build();

        appStoreProductReplyService.save(storeProductReply);
        //获取评价商品数量
        long replyCount = appStoreProductReplyService.count(new LambdaQueryWrapper<StoreProductReplyDO>().eq(StoreProductReplyDO::getOid, orderCartInfo.getOid()));
        //购买商品数量
        long cartCount = storeOrderCartInfoService.count(new LambdaQueryWrapper<StoreOrderCartInfoDO>().eq(StoreOrderCartInfoDO::getOid, orderCartInfo.getOid()));

        if (NumberUtil.equals(replyCount, cartCount)) {
            StoreOrderDO storeOrder = new StoreOrderDO();
            storeOrder.setStatus(OrderInfoEnum.STATUS_FINISHED.getValue());
            storeOrder.setId(orderCartInfo.getOid());
            this.updateById(storeOrder);
        }
        //增加状态
        storeOrderStatusService.create(uid, orderCartInfo.getOid(),
                OrderLogEnum.EVAL_ORDER.getValue(),
                OrderLogEnum.EVAL_ORDER.getDesc());

        //评价增加积分
        integralRuleContext.execute(BillDetailEnum.COMMENT, uid);
    }

    /**
     * 删除订单
     *
     * @param orderId 单号
     * @param uid     uid
     */
    @Override
    public void removeOrder(String orderId, Long uid) {
        AppStoreOrderQueryVo order = this.getOrderInfo(orderId, uid);
        if (order == null) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        order = handleOrder(order);
        if (!(OrderInfoEnum.STATUS_FINISHED.getValue().equals(order.getStatus()) ||
                OrderInfoEnum.STATUS_CANCEL.getValue().equals(order.getStatus()) ||
                OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue().equals(order.getRefundStatus())
        )) {
            throw exception(ORDER_NOT_DELETE);
        }

        this.removeById(order.getId());

        //增加状态
        storeOrderStatusService.create(uid, order.getId(),
                OrderLogEnum.REMOVE_ORDER.getValue(),
                OrderLogEnum.REMOVE_ORDER.getDesc());
    }

    /**
     * 未付款取消订单
     *
     * @param orderId 订单号
     * @param uid     用户id
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void cancelOrder(String orderId, Long uid) {
        log.info("订单取消：({})", orderId);
        AppStoreOrderQueryVo order = this.getOrderInfo(orderId, uid);
        if (ObjectUtil.isNull(order)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        if (order.getPaid() != 0) {
            throw exception(ORDER_NOT_CANCEL);
        }
        //微信h5支付商户配置
        MerchantDetailsDO merchantDetailsDO = merchantDetailsService.getMerchantByType(PayTypeEnum.WEIXIN_H5.getType());
        PayInfo payInfo = JsonUtils.parseObject(order.getPayInfo(), PayInfo.class);
        //取消之前要查询微信端是否支付完成如若由于网络原因造成两端不一致以微信端支付装态为准，已支付则不能取消，走退款逻辑
        // if (payInfo == null) throw exception(ORDER_PAYINFO_ERROR);
        // 有微信订单的数据关闭微信订单
        if (payInfo != null) {
            Map<String, String> map = PaymentApi.queryByOutTradeNo(merchantDetailsDO.getAppid(), merchantDetailsDO.getMchId(), merchantDetailsDO.getKeyPrivate(), payInfo.getOut_trade_no());
            log.info("查询微信订单状态：{}", map);
            //支付方也需要调用关闭订单接口
            if (Objects.equals(map.get(WxOrderResultEnum.TRADE_STATE.getValue()), WxOrderResultEnum.NOT_PAY.getValue())) {
                Map<String, String> closeOrderMap = PaymentApi.closeOrder(merchantDetailsDO.getAppid(), merchantDetailsDO.getMchId(), merchantDetailsDO.getKeyPrivate(), payInfo.getOut_trade_no());
                log.info("微信关闭订单：{}", closeOrderMap);
            }
        }
        // 取消订单回退积分
//        this.regressionIntegral(order, 0);
        // 取消订单回退库存
        this.regressionStock(order, StockBackTypeEnum.CANCEL.getValue());

        // 取消订单退回优惠券
        if (order.getCouponId() != null && order.getCouponId() != 0) {
            appCouponRelationService.returnCoupon(order.getCouponId());
        }
        this.regressionCoupon(order, 0);

        // 订单状态改为取消
        update(new LambdaUpdateWrapper<StoreOrderDO>()
                .eq(StoreOrderDO::getId, order.getId())
                .set(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_CANCEL.getValue()));

        // 异步统计
        asyncStoreOrderService.orderData(order.getUid());

    }

    /**
     * 确认订单退款
     *
     * @param param 退款参数
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void orderRefund(AppOrderRefundParam param) throws Exception {
        String orderId = param.getOrderId();
        BigDecimal price = param.getPrice();
        // 查询订单信息
        AppStoreOrderQueryVo orderQueryVo = getOrderInfo(orderId, null);

        StoreOrderDO updateOrderDO = new StoreOrderDO();
        // 生成退款单号
        PayInfo payInfo = JsonUtils.parseObject(orderQueryVo.getPayInfo(), PayInfo.class);
        if (payInfo == null) throw exception(ORDER_PAYINFO_ERROR);
        // 查询退款目标用户信息，有代付则为代付的用户，没有则为订单用户
        Long refundTargetUid = null;
        String billTitle = null;
        if (orderQueryVo.getHelpUid() != null) {
            refundTargetUid = orderQueryVo.getHelpUid();
            billTitle = BillTitleEnum.PRODUCT_HELP_PAY_REFUND.getValue();
        } else {
            refundTargetUid = orderQueryVo.getUid();
            billTitle = BillTitleEnum.PRODUCT_REFUND.getValue();
        }
        MemberUserDO memberUserDO = memberUserService.getById(refundTargetUid);
        String payType = orderQueryVo.getPayType();
        Long orderPriKeyId = orderQueryVo.getId();
        //根据支付类型不同退款不同
        if (price.compareTo(BigDecimal.ZERO) >= 0) {
            if (PayTypeEnum.YUE.getType().equals(payType)) {
                //退款到余额
                memberUserService.incMoney(refundTargetUid, price);
                //增加流水
                billService.income(refundTargetUid, billTitle, BillDetailEnum.CATEGORY_1.getValue(),
                        BillDetailEnum.TYPE_5.getValue(),
                        price,
                        memberUserDO.getNowMoney().add(price),
                        "订单退款到余额" + price + "元", orderPriKeyId.toString());
                //退款得仅仅退库存，其他得 不退比如优惠券等
                this.regressionStock(orderQueryVo, param.getType());
                storeOrderStatusService.create(refundTargetUid, orderPriKeyId,
                        OrderLogEnum.REFUND_ORDER_SUCCESS.getValue(), "退款给用户：" + price + "元");
            } else if (PayTypeEnum.WEIXIN.getType().equals(payType)) {
                if (payInfo.getTransaction_id() == null)
                    throw exception(ErrorCodeConstants.STORE_ORDER_REFUND_TRANSACTION_NULL_ERROR);
                String orderRefundNo = orderId + "-" + RandomStringUtil.getRandomCode(3, 0) +
                        payInfo.getOut_trade_no().substring(payInfo.getOut_trade_no().lastIndexOf('-') + 1);
                payInfo.setOut_refund_no(orderRefundNo);
                // 微信 根据不同付款方式进行退款 小程序 app
                MerchantDetailsDO merchantDetailsDO;
                if (payInfo.getOut_trade_no().contains("APPLET")) {
                    merchantDetailsDO = merchantDetailsService.getMerchantByType(PayTypeEnum.WEIXIN_APPLET.getType());
                } else {
                    merchantDetailsDO = merchantDetailsService.getMerchantByType(PayTypeEnum.WEIXIN_H5.getType());
                }
                Map<String, String> packageParams = wxPayServiceUtils.refund(payInfo.getTransaction_id(),
                        payInfo.getOut_refund_no(), orderQueryVo.getTotalPrice(), price, merchantDetailsDO);
                log.info("退款信息：{}", packageParams);
                //退款成功操作 应该在回调url中
                if (Objects.equals(packageParams.get("result_code"), "SUCCESS")) {
                    payInfo.setRefund_id(packageParams.get("refund_id"));
                    updateOrderDO.setRefundPrice(price);
                    updateOrderDO.setPayInfo(JsonUtils.toJsonString(payInfo));
                    //退款得仅仅退库存，其他得 不退比如优惠券等
                    this.regressionStock(orderQueryVo, param.getType());
                    storeOrderStatusService.create(refundTargetUid, orderPriKeyId,
                            OrderLogEnum.REFUND_ORDER_SUCCESS.getValue(), "退款给用户：" + price + "元");
                } else {
                    throw exception(STORE_ORDER_REFUND_ERROR);
                }
            }
        }
        // 修改订单信息
        updateOrderDO.setId(orderPriKeyId);
        // 修改明细状态，并获取订单下一步状态
        updateOrderDO.setRefundStatus(orderDetailService.updateState(param.getAfterSalesId(), orderPriKeyId));
        // 处理待拼团订单
        if (ObjectUtil.isNotNull(orderQueryVo.getCampaignType()) &&
                CampaignTypeEnum.TEAMWORK.getValue().equals(orderQueryVo.getCampaignType())
                && OrderInfoEnum.STATUS_WAIT_GROUP.getValue().equals(orderQueryVo.getStatus())
        ) {
            teamworkInfoService.refundHandle(new TeamworkRefundParam()
                    .setUid(refundTargetUid).setId(orderQueryVo.getTeamworkId()));
        }
        this.baseMapper.updateById(updateOrderDO);

        //确认订单退款，积分退还
        memberUserService.addIntegral(UserBillModifyDTO.builder()
                .uid(refundTargetUid)
                .amount(param.getRefundIntegral())
                .afterAmount(NumberUtil.add(memberUserDO.getIntegral(), param.getRefundIntegral()))
                .linkId(orderPriKeyId)
                .typeEnum(param.getBillEnum())
                .categoryEnum(CATEGORY_2)
                .pmType(BillEnum.PM_1)
                .build());

        // 取消订单更新分销订单状态
        appDistributorOrderFacade.cancelDistributorOrder(new DistributorOrderDTO().setOid(updateOrderDO.getId()));
    }

    @Override
    public void batchRefund(List<Long> ids, Integer type) throws Exception {
        List<StoreOrderDO> orders = storeOrderMapper.selectBatchIds(ids);
        if (CollectionUtils.isNotEmpty(orders)) {
            for (StoreOrderDO order : orders) {
                this.orderRefund(new AppOrderRefundParam(
                                null,
                                order.getOrderId(),
                                order.getPayPrice(),
                                type,
                                order.getPayIntegral(),
                                TYPE_13
                        )
                );
            }
        }
    }

    @Override
    public void writeOff(LoginUser user, WriteOffDTO dto) {
        ShopAssistantDO shopAssistantDO = shopAssistantService.selectByUserId(user.getId());
        // 用户校验权限
        if (UserTypeEnum.MEMBER.getValue().equals(user.getUserType()) && !shopAssistantDO.getStatus()) {
            throw exception(SHOP_ASSISTANT_NOT_WRITE_OFF);
        }
        //查询订单
        StoreOrderDO storeOrderDO = storeOrderMapper.selectOne(StoreOrderDO::getWriteOffCode, dto.getWriteOffCode());
        if (Objects.isNull(storeOrderDO)) {
            throw exception(ORDER_WRITE_OFF_CODE_ERROR);
        }
        if (!storeOrderDO.getWriteOffStatus().equals(TO_BE_WRITTEN_OFF.getValue())) {
            throw exception(ORDER_WRITTEN_OFF);
        }
        //门店用户校验
        if (UserTypeEnum.MEMBER.getValue().equals(user.getUserType()) && !shopAssistantDO.getStoreId().equals(storeOrderDO.getStoreId())) {
            throw exception(NO_WRITE_OFF_AUTHORITY);
        }
        //校验核销码
        if (!storeOrderDO.getWriteOffCode().equals(dto.getWriteOffCode()))
            throw exception(ORDER_WRITE_OFF_CODE_ERROR);
        //更新订单状态
        StoreOrderDO newOrder = new StoreOrderDO();
        newOrder.setId(storeOrderDO.getId());
        newOrder.setWriteOffStatus(WRITTEN_OFF.getValue());
        newOrder.setWriteOffDate(LocalDateTime.now());
        newOrder.setStatus(OrderInfoEnum.STATUS_FINISHED.getValue());
        storeOrderMapper.updateById(newOrder);

        //增加状态
        storeOrderStatusService.create(storeOrderDO.getUid(), storeOrderDO.getId(), OrderLogEnum.TAKE_ORDER_DELIVERY.getValue(), OrderLogEnum.TAKE_ORDER_DELIVERY.getDesc());
        //核销完成
        Integer closeAfterSaleDays = storeOrderRuleService.getOrderRuleCache().getNoAfterSalesTime();
        OrderDefaultRuleEnum orderMaxCanAfterSaleTimeEnum = OrderDefaultRuleEnum.ORDER_MAX_CAN_AFTER_SALE_TIME;
        redissonUtil.delayedOffer(orderMaxCanAfterSaleTimeEnum.getKey(), storeOrderDO.getId(), closeAfterSaleDays, orderMaxCanAfterSaleTimeEnum.getUnit());

    }

    @Override
    public AppStoreOrderQueryVo orderDetail(String key, Long uid) {
        AppStoreOrderQueryVo storeOrder = this.getOrderInfo(key, uid);
        if (ObjectUtil.isNull(storeOrder)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        //订单状态
        this.handleOrder(storeOrder);
        //门店信息
        ShopDO shop = shopService.getShop(storeOrder.getStoreId());
        if (Objects.nonNull(shop)) {
            storeOrder.setStoreName(shop.getStoreName());
            storeOrder.setShopAddress(shop.getAddress());
        }
        return storeOrder;
    }

    @Override
    public void teamworkEnd(Long teamworkId) throws Exception {
        // 获取拼团活动信息
        CampaignInfoDO campaignInfoDO = teamworkInfoService.getCampaignInfoById(teamworkId);
        // 获取该拼团的所有订单
        List<StoreOrderDO> orderDOS =
                storeOrderMapper.selectList(new LambdaQueryWrapper<StoreOrderDO>()
                        .eq(StoreOrderDO::getTeamworkId, teamworkId));
        LambdaUpdateWrapper<StoreOrderDO> updateWrapper = new LambdaUpdateWrapper<StoreOrderDO>();
        updateWrapper.eq(StoreOrderDO::getTeamworkId, teamworkId);
        // 判断是否虚拟拼团
        if (VirtuallyEnum.YES.getValue().equals(campaignInfoDO.getIsVirtually())) {
            // 拼团成功，订单状态改为已发货
            updateWrapper.set(StoreOrderDO::getStatus, OrderInfoEnum.STATUS_DEFAULT.getValue());
            // 修改拼团状态
            teamworkInfoService.teamworkSuccess(teamworkId);
        } else {
            // 拼团失败，订单状态改为成团失败
            updateWrapper.set(StoreOrderDO::getStatus, OrderStatusEnum.STATUS_GROUP_FAILURE.getValue());
            updateWrapper.set(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue());
            // 退款，不退库存，退到活动库存
            List<Long> orderIds = orderDOS.stream().map(StoreOrderDO::getId).collect(Collectors.toList());
            batchRefund(orderIds, StockBackTypeEnum.DEFAULT.getValue());
            // 修改拼团状态
            teamworkInfoService.teamworkFail(teamworkId, orderDetailMapper.selectCount(
                    new LambdaQueryWrapper<StoreOrderDetailDO>().in(StoreOrderDetailDO::getOid, orderIds)).intValue());
        }
        // 修改订单状态
        storeOrderMapper.update(null, updateWrapper);
    }

    @SneakyThrows
    @Override
    public void refundOrder(String id, Long uid) {
        // 查询订单信息
        AppStoreOrderQueryVo orderQueryVo = getOrderInfo(id, null);
        if (Objects.isNull(orderQueryVo)) {
            throw exception(STORE_ORDER_NOT_EXISTS);
        }
        //判断订单状态是否为已经支付
        if (OrderInfoEnum.PAY_STATUS_UNPAID.getValue().equals(orderQueryVo.getPaid())) {
            throw exception(ORDER_NOT_REVOKE);
        }
        AppOrderRefundParam param = new AppOrderRefundParam();
        param.setOrderId(id);
        param.setType(StockBackTypeEnum.DEFAULT.getValue());
        param.setPrice(orderQueryVo.getPayPrice());
        param.setRefundIntegral(orderQueryVo.getPayIntegral());
        param.setBillEnum(TYPE_13);
        this.orderRefund(param);
    }

    @Override
    public AppUserOrderCountVo orderStatistics(Long uid) {
        // 查询所有未退款订单
        List<StoreOrderDO> storeOrderDOS =
                storeOrderMapper.selectList(new LambdaQueryWrapper<StoreOrderDO>()
                        .eq(StoreOrderDO::getUid, uid)
                        .eq(StoreOrderDO::getRefundStatus, OrderInfoEnum.REFUND_STATUS_NORMAL.getValue()));
        // 计算订单支付没有退款总数
        Long orderCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid())).count();
        // 计算待付款订单数
        Long unpaidCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_UNPAID.getValue().equals(order.getPaid()) &&
                        OrderInfoEnum.STATUS_DEFAULT.getValue().equals(order.getStatus())).count();
        // 计算待发货订单数
        Long unshippedCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid()) &&
                        OrderInfoEnum.STATUS_DEFAULT.getValue().equals(order.getStatus())).count();
        // 计算待收货订单数
        Long receivedCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid()) &&
                        OrderInfoEnum.STATUS_WAIT_RECEIVED.getValue().equals(order.getStatus())).count();
        // 计算待评价订单数
        Long evaluatedCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid()) &&
                        OrderInfoEnum.STATUS_RECEIVED.getValue().equals(order.getStatus())).count();
        // 计算订单完成数量
        Long completeCount = storeOrderDOS.stream().filter(order ->
                OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid()) &&
                        OrderInfoEnum.STATUS_FINISHED.getValue().equals(order.getStatus())).count();
        AppUserOrderCountVo appUserOrderCountVo = AppUserOrderCountVo.builder()
                .orderCount(orderCount)
                .unpaidCount(unpaidCount)
                .unshippedCount(unshippedCount)
                .receivedCount(receivedCount)
                .evaluatedCount(evaluatedCount)
                .completeCount(completeCount)
                .build();
        return appUserOrderCountVo;
    }


    /**
     * 退回积分
     *
     * @param order 订单
     */
    private void regressionIntegral(AppStoreOrderQueryVo order, Integer type) {
        if (type == 0) {
            if (OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid())
                    || OrderStatusEnum.STATUS_MINUS_REFUNDED.getValue().equals(order.getStatus())) {
                return;
            }
        } else {
            if (!(OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid())
                    && OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue().equals(order.getRefundStatus()))) {
                return;
            }
        }

        if (order.getPayIntegral().compareTo(BigDecimal.ZERO) > 0) {
            order.setUseIntegral(order.getPayIntegral());
        }
        if (order.getUseIntegral().compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }

        if (!OrderStatusEnum.STATUS_MINUS_REFUNDED.getValue().equals(order.getStatus())
                && !OrderInfoEnum.REFUND_STATUS_HAVE_REFUNDED.getValue().equals(order.getRefundStatus())
                && order.getBackIntegral().compareTo(BigDecimal.ZERO) > 0) {
            return;
        }

        MemberUserDO user = memberUserService.getUser(order.getUid());
        //取消订单，退回积分
//        memberUserService.addIntegral(UserBillModifyDTO.builder()
//                .uid(order.getUid())
//                .amount(order.getUseIntegral())
//                .afterAmount(NumberUtil.add(user.getIntegral(), order.getUseIntegral()))
//                .linkId(order.getId())
//                .typeEnum(TYPE_13)
//                .categoryEnum(CATEGORY_2)
//                .pmType(BillEnum.PM_1)
//                .build());

        //更新回退积分
        StoreOrderDO storeOrder = new StoreOrderDO();
        storeOrder.setBackIntegral(order.getUseIntegral());
        storeOrder.setId(order.getId());
        this.updateById(storeOrder);
    }

    /**
     * 退回优惠券
     *
     * @param order 订单 todo
     */
    private void regressionCoupon(AppStoreOrderQueryVo order, Integer type) {

    }

    /**
     * 退回库存
     *
     * @param order
     * @param type
     */
    private void regressionStock(AppStoreOrderQueryVo order, Integer type) {
        if (StockBackTypeEnum.CANCEL.getValue().equals(type)) {
            if (OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid())
                    || OrderStatusEnum.STATUS_MINUS_REFUNDED.getValue().equals(order.getStatus())) {
                return;
            }
        } else {
            if (!OrderInfoEnum.PAY_STATUS_HAVE_PAID.getValue().equals(order.getPaid())) {
                return;
            }
        }
        LambdaQueryWrapper<StoreOrderCartInfoDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(StoreOrderCartInfoDO::getCartId, Arrays.asList(order.getCartId().split(",")));
        List<StoreOrderCartInfoDO> cartInfoList = storeOrderCartInfoService.list(wrapper);

        type = ObjectUtil.isNotNull(order.getCampaignType()) && !StockBackTypeEnum.NORMAL.getValue().equals(type) ?
                StockBackTypeEnum.CAMPAIGN.getValue() : StockBackTypeEnum.NORMAL.getValue();
        for (StoreOrderCartInfoDO cartInfo : cartInfoList) {
            AppStoreCartQueryVo cart = JSONObject.parseObject(cartInfo.getCartInfo()
                    , AppStoreCartQueryVo.class);
            appStoreProductService.incProductStock(cart.getCartNum(), cart.getProductId(),
                    cart.getProductAttrUnique(), type);
            //库存预警检测
            storeProductStockWarningService.checkStockWarning(cart.getProductId(),cart.getProductAttrUnique());
        }
    }


    /**
     * 订单检测
     *
     * @param uid uid
     * @param key 缓存值
     * @return map
     */
    private Map<String, Object> check(Long uid, String key) {
        Map<String, Object> map = Maps.newHashMap();
        if (StrUtil.isBlank(key)) {
            throw exception(PARAM_ERROR);
        }
        AppStoreOrderQueryVo storeOrder = this.getOrderInfo(key, uid);
        if (ObjectUtil.isNotNull(storeOrder)) {

            OrderExtendDTO orderExtendDTO = OrderExtendDTO.builder()
                    .key(key)
                    .orderId(storeOrder.getOrderId())
                    .build();
            map.put("status", OrderLogEnum.EXTEND_ORDER.getValue());
            map.put("result", orderExtendDTO);
            map.put("msg", OrderLogEnum.EXTEND_ORDER.getDesc());
        }


        return map;
    }


    /**
     * 获取订单价格
     *
     * @param user         用户信息
     * @param cartInfo     购物车信息
     * @param userAddress  用户地址
     * @param couponId     优惠券
     * @param shippingType 下单类型 邮寄/自提
     * @param useIntegral  是否使用积分
     * @return /
     */
    private PriceGroupDTO getOrderPriceGroup(MemberUserDO user, List<AppStoreCartQueryVo> cartInfo, UserAddressDO userAddress, Long couponId, Integer shippingType, Boolean useIntegral) {
        PriceGroupDTO priceGroupDTO = new PriceGroupDTO();

        //获取订单成本价
        BigDecimal costPrice = this.getOrderSumPrice(cartInfo, "truePrice");

        BigDecimal storePostage = BigDecimal.ZERO;
        BigDecimal storeFreePostage = BigDecimal.ZERO;
        BigDecimal couponPrice = BigDecimal.ZERO;
        //总价格
        BigDecimal totalPrice = costPrice;
        //计算会员价格
        BigDecimal vipDeductionAmount = culVipDeductionAmount(user.getId(), costPrice, priceGroupDTO);
        totalPrice = NumberUtil.sub(totalPrice, vipDeductionAmount);
        //会员折扣
        BigDecimal vipDiscount = NumberUtil.mul(priceGroupDTO.getVipDiscount(), new BigDecimal("0.1"));

        //计算优惠券
        if (couponId == null || couponId != -1L) {
            List<CartCouponDTO> cartCouponDtoList = new ArrayList<>();
            for (AppStoreCartQueryVo vo : cartInfo) {
                cartCouponDtoList.add(new CartCouponDTO(vo.getProductId(), vo.getProductAttrUnique(), NumberUtil.mul(vo.getTruePrice(), vo.getCartNum(), vipDiscount)));
            }
            List<CartCouponVO> cartCouponVOList = appCouponRelationService.searchCartCoupon(cartCouponDtoList, user.getId(), couponId);
            if (CollectionUtils.isNotEmpty(cartCouponVOList)) {
                CartCouponVO cartCouponVO = cartCouponVOList.get(0);
                couponPrice = cartCouponVO.getDiscountAmount();
                couponId = cartCouponVO.getDetailId();
                List<CartCouponDetailVO> cartCouponDetailVOList = cartCouponVO.getDetailVOList();
                if (CollectionUtils.isNotEmpty(cartCouponDetailVOList)) {
                    Map<String, CartCouponDetailVO> cartCouponDetailVOMap =
                            cartCouponDetailVOList.stream().collect(Collectors.toMap(CartCouponDetailVO::getProductAttrUnique, Function.identity()));
                    cartInfo.forEach(res -> {
                        if (ObjectUtil.isNotNull(cartCouponDetailVOMap.get(res.getProductAttrUnique()))) {
                            res.setCouponPrice(cartCouponDetailVOMap.get(res.getProductAttrUnique()).getProductCoupon());
                        }
                    });
                }
            }
        }
        totalPrice = NumberUtil.sub(totalPrice, couponPrice);
        //计算积分
        BigDecimal deductionAmount = culIntegral(user, totalPrice, priceGroupDTO, useIntegral);
        log.info("积分抵扣金额:{}", deductionAmount);
        //计算每个明细的真实价格/使用积分
        BigDecimal finalTotalPrice = totalPrice;
        cartInfo.forEach(res -> {
            // 优惠券优惠金额
            BigDecimal itemCouponPrice = NumberUtil.div(Objects.isNull(res.getCouponPrice()) ? BigDecimal.ZERO : res.getCouponPrice(), res.getCartNum());
            res.setCouponPrice(itemCouponPrice);
            //真实价格 = 真实价格 - 会员优惠价格 - 优惠券优惠金额 - 积分抵扣金额
            BigDecimal payPrice = res.getTruePrice();
            payPrice = NumberUtil.sub(NumberUtil.mul(payPrice, vipDiscount), itemCouponPrice);
            if (finalTotalPrice.compareTo(BigDecimal.ZERO) > 0) {
                // 占比
                BigDecimal div = NumberUtil.div(payPrice, finalTotalPrice);
                //每项的积分抵扣金额
                BigDecimal itemIntegralDeductionAmount = NumberUtil.mul(deductionAmount, div);
                //每项使用积分
                BigDecimal itemUseIntegral = NumberUtil.mul(priceGroupDTO.getPayIntegral(), div);
                payPrice = NumberUtil.sub(payPrice, itemIntegralDeductionAmount);
                res.setIntegralDeductionAmount(itemIntegralDeductionAmount);
                res.setUseIntegral(itemUseIntegral);
            }
            res.setPayPrice(payPrice);
        });

        // 计算运费
        if (!OrderInfoEnum.SHIPPING_TYPE_STORE_PICKUP.getValue().equals(shippingType)) {
            for (AppStoreCartQueryVo vo : cartInfo) {
                BigDecimal postagePrice = this.handlePostage(Arrays.asList(vo), userAddress);
                vo.setPostagePrice(postagePrice);
                storePostage = storePostage.add(postagePrice);
            }
        }
        //会员是否包邮
        if (priceGroupDTO.getVipFreeShipping()) {
            storeFreePostage = storePostage;
            cartInfo.forEach(res -> res.setPostagePrice(BigDecimal.ZERO));
            storePostage = BigDecimal.ZERO;
        }

        totalPrice = totalPrice.add(storePostage).subtract(deductionAmount);//获取订单总金额

        priceGroupDTO.setStorePostage(storePostage);
        priceGroupDTO.setStoreFreePostage(storeFreePostage);
        priceGroupDTO.setTotalPrice(totalPrice);
        priceGroupDTO.setCostPrice(costPrice);
        priceGroupDTO.setCouponPrice(couponPrice);
        priceGroupDTO.setCouponId(couponId);
        priceGroupDTO.setTotalCouponPrice(NumberUtil.add(priceGroupDTO.getCouponPrice(), priceGroupDTO.getVipDeductionAmount(), priceGroupDTO.getDeductionPrice()));
        return priceGroupDTO;
    }

    /**
     * 获取活动订单价格
     *
     * @param cartInfo     购物车信息
     * @param userAddress  用户地址对象
     * @param shippingType 配送方式 1=快递 ，2=门店自提
     * @return
     */
    private PriceGroupDTO getOrderPriceGroupForCampaign(MemberUserDO user, List<AppStoreCartQueryVo> cartInfo, UserAddressDO userAddress, Integer shippingType) {
        PriceGroupDTO priceGroupDTO = new PriceGroupDTO();

        //获取订单成本价
        BigDecimal costPrice = this.getOrderSumPrice(cartInfo, "truePrice");
        BigDecimal totalPrice = costPrice;
        //运费
        BigDecimal storePostage = BigDecimal.ZERO;
        BigDecimal storeFreePostage = BigDecimal.ZERO;
        // 计算运费
        if (!OrderInfoEnum.SHIPPING_TYPE_STORE_PICKUP.getValue().equals(shippingType)) {
            for (AppStoreCartQueryVo vo : cartInfo) {
                BigDecimal postagePrice = this.handlePostage(Arrays.asList(vo), userAddress);
                vo.setPostagePrice(postagePrice);
                storePostage = storePostage.add(postagePrice);
            }
        }
        totalPrice = totalPrice.add(storePostage);//获取订单总金额

        priceGroupDTO.setStorePostage(storePostage);
        priceGroupDTO.setStoreFreePostage(storeFreePostage);
        priceGroupDTO.setTotalPrice(totalPrice);
        priceGroupDTO.setCostPrice(costPrice);
        BigDecimal integralTotal = BigDecimal.ZERO;
        for (AppStoreCartQueryVo appStoreCartQueryVo : cartInfo) {
            Integer cartNum = appStoreCartQueryVo.getCartNum();
            BigDecimal useIntegral = appStoreCartQueryVo.getUseIntegral();
            integralTotal = integralTotal.add((useIntegral == null ? BigDecimal.ZERO : useIntegral).multiply(BigDecimal.valueOf(cartNum)));
        }
        //需要支付的积分
        priceGroupDTO.setPayIntegral(integralTotal);
        //用户的当前积分
        if (integralTotal.compareTo(BigDecimal.ZERO) != 0) {
            priceGroupDTO.setIntegral(user.getIntegral());
        }
        cartInfo.forEach(item -> item.setPayPrice(item.getTruePrice()));

        return priceGroupDTO;
    }

    /**
     * 计算积分相关
     *
     * @param user          /
     * @param totalPrice    /
     * @param priceGroupDTO /
     * @return /
     */
    private BigDecimal culIntegral(MemberUserDO user, BigDecimal totalPrice, PriceGroupDTO priceGroupDTO, Boolean useIntegral) {
        Boolean enableIntegral = "1".equals(DictFrameworkUtils.parseDictDataValue(INTEGRAL_CONFIG, INTEGRAL_ENABLE));
        BigDecimal integralRule = new BigDecimal(DictFrameworkUtils.parseDictDataValue(INTEGRAL_CONFIG, INTEGRAL_DEDUCTION_RULE));
        //用户剩余积分
        BigDecimal integral = user.getIntegral();

        priceGroupDTO.setEnableIntegral(enableIntegral);
        priceGroupDTO.setIntegral(integral);
        //积分抵扣关闭 或者 用户积分小于积分规则 不可抵扣
        if (!useIntegral || !enableIntegral || integral.compareTo(integralRule) < 0) {
            priceGroupDTO.setPayIntegral(BigDecimal.ZERO);
            priceGroupDTO.setDeductionPrice(BigDecimal.ZERO);
            return BigDecimal.ZERO;
        }
        BigDecimal fullDeductionAmount = NumberUtil.div(integral, integralRule);
        BigDecimal integralDeductionAmount = totalPrice.compareTo(fullDeductionAmount) <= 0 ? totalPrice : fullDeductionAmount;
        BigDecimal payIntegral = NumberUtil.mul(integralDeductionAmount, integralRule);

        priceGroupDTO.setIntegralRule(integralRule);
        priceGroupDTO.setPayIntegral(payIntegral);
        priceGroupDTO.setDeductionPrice(integralDeductionAmount);
        return integralDeductionAmount;
    }

    /**
     * 计算vip优惠金额
     *
     * @param uid           /
     * @param costPrice     /
     * @param priceGroupDTO /
     * @return /
     */
    private BigDecimal culVipDeductionAmount(Long uid, BigDecimal costPrice, PriceGroupDTO priceGroupDTO) {
        SettleLevelInfoVO settleLevelInfo = userLevelConfigService.settleLevelInfo(uid);
        priceGroupDTO.setIsVip(settleLevelInfo.getIsVip());
        priceGroupDTO.setVipFreeShipping(settleLevelInfo.getFreeShipping());
        priceGroupDTO.setVipDiscount(settleLevelInfo.getDiscount());
        if (!settleLevelInfo.getIsVip()) {
            return BigDecimal.ZERO;
        }
        BigDecimal vipDeductionAmount = NumberUtil.mul(costPrice, NumberUtil.sub(BigDecimal.ONE, NumberUtil.mul(new BigDecimal("0.1"), settleLevelInfo.getDiscount())));
        priceGroupDTO.setVipDeductionAmount(vipDeductionAmount);
        return vipDeductionAmount;
    }

    /**
     * 获取某字段价格
     *
     * @param cartInfo 购物车
     * @param key      key值
     * @return Double
     */
    private BigDecimal getOrderSumPrice(List<AppStoreCartQueryVo> cartInfo, String key) {
        BigDecimal sumPrice = BigDecimal.ZERO;

        if ("truePrice".equals(key)) {
            for (AppStoreCartQueryVo storeCart : cartInfo) {
                sumPrice = NumberUtil.add(sumPrice, NumberUtil.mul(storeCart.getCartNum(), storeCart.getTruePrice()));
            }
        } else if ("costPrice".equals(key)) {
            for (AppStoreCartQueryVo storeCart : cartInfo) {
                sumPrice = NumberUtil.add(sumPrice, NumberUtil.mul(storeCart.getCartNum(), storeCart.getCostPrice()));
            }
        } else if ("payIntegral".equals(key)) {
            for (AppStoreCartQueryVo storeCart : cartInfo) {
                if (storeCart.getProductInfo().getAttrInfo() != null && storeCart.getProductInfo().getAttrInfo().getIntegral() != null) {
                    sumPrice = NumberUtil.add(sumPrice,
                            NumberUtil.mul(storeCart.getCartNum(), storeCart.getProductInfo().getAttrInfo().getIntegral()));
                }

            }
        }

        return sumPrice;
    }


    /**
     * 根据运费模板算法返回邮费
     *
     * @param cartInfo    购物车
     * @param userAddress 地址
     * @return double
     */
    public BigDecimal handlePostage(List<AppStoreCartQueryVo> cartInfo, UserAddressDO userAddress) {
        BigDecimal storePostage = BigDecimal.ZERO;
        if (userAddress != null) {
            if (userAddress.getCityId() == null) {
                return storePostage;
            }
            //城市包括默认
            int cityId = userAddress.getCityId();
            List<Integer> cityIdList = new ArrayList<>();
            cityIdList.add(cityId);
            cityIdList.add(0);

            List<AppStoreProductRespVo> storeProductVOList = cartInfo
                    .stream()
                    .map(AppStoreCartQueryVo::getProductInfo)
                    .collect(Collectors.toList());
            List<Integer> tempIdS = storeProductVOList
                    .stream()
                    .map(AppStoreProductRespVo::getTempId)
                    .collect(Collectors.toList());


            //获取商品用到的运费模板
            List<ShippingTemplatesDO> shippingTemplatesList = shippingTemplatesService
                    .list(Wrappers.<ShippingTemplatesDO>lambdaQuery()
                            .in(ShippingTemplatesDO::getId, tempIdS));
            //获取运费模板区域列表按照城市排序
            List<ShippingTemplatesRegionDO> shippingTemplatesRegionList = shippingTemplatesRegionService
                    .list(Wrappers.<ShippingTemplatesRegionDO>lambdaQuery()
                            .in(ShippingTemplatesRegionDO::getTempId, tempIdS)
                            .in(ShippingTemplatesRegionDO::getCityId, cityIdList)
                            .orderByAsc(ShippingTemplatesRegionDO::getCityId));
            //提取运费模板类型
            Map<Integer, Integer> shippingTemplatesMap = shippingTemplatesList
                    .stream()
                    .collect(Collectors.toMap(ShippingTemplatesDO::getId,
                            ShippingTemplatesDO::getType));
            //提取运费模板有相同值覆盖
            Map<Integer, ShippingTemplatesRegionDO> shippingTemplatesRegionMap =
                    shippingTemplatesRegionList.stream()
                            .collect(Collectors.toMap(ShippingTemplatesRegionDO::getTempId,
                                    ShippingTemplatesRegionDO -> ShippingTemplatesRegionDO,
                                    (key1, key2) -> key2));


            Map<Integer, TemplateDTO> templateDTOMap = new HashMap<>();
            for (AppStoreCartQueryVo storeCartVO : cartInfo) {
                Integer tempId = storeCartVO.getProductInfo().getTempId();

                //是否是固定邮费,没设置运费模板走固定
                if (tempId == 0) {
                    /*storePostage =
                            storePostage.add(storeCartVO.getProductInfo().getPostage()
                                    .multiply(BigDecimal.valueOf(storeCartVO.getCartNum())));
                    continue;*/
                    return storeCartVO.getProductInfo().getPostage();
                }

                //根据模板类型获取相应的数量
                double num = 0d;
                if (ShippingTempEnum.BY_PIECE.getValue().equals(shippingTemplatesMap.get(tempId))) {
                    num = storeCartVO.getCartNum().doubleValue();
                } else if (ShippingTempEnum.BY_WEIGHT.getValue().equals(shippingTemplatesMap.get(tempId))) {
                    num = NumberUtil.mul(storeCartVO.getCartNum(),
                            storeCartVO.getProductInfo().getAttrInfo().getWeight()).doubleValue();
                } else if (ShippingTempEnum.BY_VOLUME.getValue().equals(shippingTemplatesMap.get(tempId))) {
                    num = NumberUtil.mul(storeCartVO.getCartNum(),
                            storeCartVO.getProductInfo().getAttrInfo().getVolume()).doubleValue();
                }

                ShippingTemplatesRegionDO shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
                BigDecimal price = NumberUtil.round(NumberUtil.mul(storeCartVO.getCartNum(),
                        storeCartVO.getTruePrice()), 2);
                if (!templateDTOMap.containsKey(tempId)) {
                    TemplateDTO templateDTO = TemplateDTO.builder()
                            .number(num)
                            .price(price)
                            .first(shippingTemplatesRegion.getFirst().doubleValue())
                            .firstPrice(shippingTemplatesRegion.getFirstPrice())
                            ._continue(shippingTemplatesRegion.getContinues().doubleValue())
                            .continuePrice(shippingTemplatesRegion.getContinuePrice())
                            .tempId(tempId)
                            .cityId(cityId)
                            .minPrice(price)
                            .build();
                    templateDTOMap.put(tempId, templateDTO);
                } else {
                    TemplateDTO templateDTO = templateDTOMap.get(tempId);
                    templateDTO.setNumber(templateDTO.getNumber() + num);
                    templateDTO.setPrice(NumberUtil.add(templateDTO.getPrice().doubleValue(), price));
                    templateDTO.setMinPrice(NumberUtil.min(templateDTO.getMinPrice(), price));
                }


            }

            //处理包邮情况
            for (Map.Entry<Integer, TemplateDTO> entry : templateDTOMap.entrySet()) {
                Integer mapKey = entry.getKey();
                TemplateDTO mapValue = entry.getValue();

                LambdaQueryWrapper<ShippingTemplatesFreeDO> wrapper = Wrappers.<ShippingTemplatesFreeDO>lambdaQuery()
                        .eq(ShippingTemplatesFreeDO::getTempId, mapValue.getTempId())
                        .eq(ShippingTemplatesFreeDO::getCityId, mapValue.getCityId())
                        .le(ShippingTemplatesFreeDO::getNumber, mapValue.getNumber())
                        .le(ShippingTemplatesFreeDO::getPrice, mapValue.getMinPrice());

                long count = shippingTemplatesFreeService.count(wrapper);
                //满足包邮条件剔除
                if (count > 0) {
                    templateDTOMap.remove(mapKey);
                    break;
                }
            }

            //处理区域邮费
            boolean isFirst = true; //用来是否多个产品的标识 false表示数量大于1
            for (TemplateDTO templateDTO : templateDTOMap.values()) {
                if (isFirst) {//首件
                    //只满足首件
                    if (Double.compare(templateDTO.getNumber(), templateDTO.getFirst()) <= 0) {
                        storePostage = NumberUtil.round(NumberUtil.add(storePostage,
                                templateDTO.getFirstPrice()), 2);
                    } else {
                        BigDecimal firstPrice = NumberUtil.add(storePostage, templateDTO.getFirstPrice());

                        if (templateDTO.get_continue() <= 0) {
                            storePostage = firstPrice;
                        } else {
                            //续件平均值且向上取整数
                            double average = Math.ceil(NumberUtil.div(NumberUtil.sub(templateDTO.getNumber(),
                                            templateDTO.getFirst()),
                                    templateDTO.get_continue().doubleValue()));
                            //最终邮费
                            storePostage = NumberUtil.add(firstPrice, NumberUtil.mul(average,
                                    templateDTO.getContinuePrice()));
                        }

                    }

                    isFirst = false;
                } else {
                    //多件直接在以前的基数继续续建
                    if (templateDTO.get_continue() > 0) {
                        //续件平均值且向上取整数
                        double average = Math.ceil(
                                NumberUtil.div(
                                        templateDTO.getNumber(),
                                        templateDTO.get_continue()
                                )
                        );
                        //最终邮费
                        storePostage = NumberUtil.add(storePostage.doubleValue(), NumberUtil.mul(average,
                                templateDTO.getContinuePrice()));
                    }
                }
            }
        }


        return storePostage;
    }

    /**
     * 活动库存操作
     *
     * @param cartInfo
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void campaignStock(List<AppStoreCartQueryVo> cartInfo) {
        log.info("========活动商品减库存增加销量start=========");
        //对库存加锁
        RLock lock = redissonClient.getLock(STOCK_LOCK_KEY);
        if (lock.tryLock()) {
            try {
                for (AppStoreCartQueryVo storeCartVO : cartInfo) {
                    // 减少活动库存
                    appCampaignInfoService.deStock(storeCartVO.getProductAttrUnique(), storeCartVO.getCartNum());
                    appStoreProductService.incSales(storeCartVO.getCartNum(), storeCartVO.getProductId());
                }
            } catch (Exception ex) {
                log.error("[campaignStock][执行异常]", ex);
                throw exception(new ErrorCode(999999, ex.getMessage()));
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    public Boolean editAddress(Long uid, AppEditOrderAddressParam param) {
        Long orderId = param.getOrderId();
        StoreOrderDO storeOrderDO = storeOrderMapper.selectOne(
                Wrappers.<StoreOrderDO>lambdaQuery()
                        .eq(StoreOrderDO::getOrderId, orderId).eq(StoreOrderDO::getUid, uid)
        );
        if (storeOrderDO.getStatus() == 0) {
            //修改订单地址
            UserAddressDO addressDO = appUserAddressService.getById(param.getAddressId());
            String address = StrUtil.concat(true, addressDO.getProvince(), " ", addressDO.getCity(), " ", addressDO.getDistrict(), " ", addressDO.getDetail());
            StoreOrderDO update = StoreOrderDO.builder().id(storeOrderDO.getId())
                    .realName(addressDO.getRealName())
                    .userPhone(addressDO.getPhone())
                    .userAddress(address)
                    .build();
            storeOrderMapper.updateById(update);
            return true;
        }
        throw exception(ORDER_NOT_UPDATE_ADDRESS);
    }

    @Override
    public Long countOrderByProduct(Long productId) {
        return appStoreCartService.count(Wrappers.lambdaQuery(StoreCartDO.class).eq(StoreCartDO::getProductId,productId));
    }
}
